├── .gitignore
├── Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)
├── Beyond the basics - Causal Discovery (causalpython.io).ipynb
├── Beyond the basics - LiNGAM experiment (causalpython.io).ipynb
├── README.md
├── causality-causalpython-py38.yml
└── img
│ ├── CausalPython.io__flat.png
│ └── glymour_et_al_pc.jpg
├── Causal Python - Did Elon Musk's Tweet Change Our Googling Habits
├── Causal Python - Did Elon Musks Tweet Change Our Googling Habits.ipynb
├── README.md
├── causal-pymc.yml
└── data
│ └── gt_social_media_data.csv
├── Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today
├── Causal Python -- 3 Simple Techniques to Jump-Start Your Causal Inference Journey Today.ipynb
├── causal-kung-fu-01.yml
├── causal_model.png
└── img
│ └── CausalPython.io__flat.png
├── Jane the Discoverer
├── Graph Plotting.ipynb
├── Jane The Discoverer.ipynb
├── causal-nlp-openai-langchain.yml
├── img
│ └── CausalPython.io__flat.png
├── true_graph.png
└── true_graph.png.png
├── LICENSE
├── Level Up - A Simple Blueprint to Predict Causal Effects for Unseen Data Using Python
├── CATE.ipynb
└── data
│ ├── hillstrom.csv
│ └── hillstrom_clean.csv
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/README.md:
--------------------------------------------------------------------------------
1 | # Causal Python - Level Up Your Causal Discovery Skills (2023)
2 |
3 |
4 | ## Installation
5 |
6 |
7 | To install the environment:
8 |
9 | `conda env create --file causality-causalpython-py38.yml`
10 |
11 | After installation the kernel should be available in your Jupyter Notebook kernel list as
12 | `causality-causalpython-py38`
--------------------------------------------------------------------------------
/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/causality-causalpython-py38.yml:
--------------------------------------------------------------------------------
1 | name: causality-causalpython-py38
2 | channels:
3 | - defaults
4 | - pytorch
5 | - conda-forge
6 | dependencies:
7 | - pip=21.0.1
8 | - python=3.8
9 | - nb_conda=2.2.1
10 | - numpy=1.20.3
11 | - pandas=1.3.1
12 | - matplotlib=3.5.0
13 | - networkx=2.8.8
14 | - seaborn=0.11.2
15 | - pytorch=1.10.1
16 | - cudatoolkit=11.3
17 | - pip:
18 | - econml==0.12
19 | - dowhy==0.8
20 | - gcastle==1.0.3
--------------------------------------------------------------------------------
/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/img/CausalPython.io__flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/img/CausalPython.io__flat.png
--------------------------------------------------------------------------------
/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/img/glymour_et_al_pc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023)/img/glymour_et_al_pc.jpg
--------------------------------------------------------------------------------
/Causal Python - Did Elon Musk's Tweet Change Our Googling Habits/README.md:
--------------------------------------------------------------------------------
1 | To install the environment:
2 |
3 | `conda env create -f causal-pymc.yml`
4 |
5 | Your new environment will be available as `causal-pymc` in the Jupyter notebook kernel list.
--------------------------------------------------------------------------------
/Causal Python - Did Elon Musk's Tweet Change Our Googling Habits/causal-pymc.yml:
--------------------------------------------------------------------------------
1 | name: causal-pymc
2 | channels:
3 | - conda-forge
4 | - defaults
5 | dependencies:
6 | - python=3.9
7 | - numba
8 | - pip
9 | - nb_conda
10 | - arviz
11 | - matplotlib
12 | - pandas
13 | - scipy
14 | - numpy
15 | - statsmodels
16 | - pydot
17 | - tqdm
18 | - ipywidgets
19 | - pip:
20 | - pymc>=4
21 | - CausalPy==0.0.8
--------------------------------------------------------------------------------
/Causal Python - Did Elon Musk's Tweet Change Our Googling Habits/data/gt_social_media_data.csv:
--------------------------------------------------------------------------------
1 | date,twitter,linkedin,tiktok,instagram
2 | 2022-05-15,55,9,23,59
3 | 2022-05-16,54,18,20,59
4 | 2022-05-17,54,20,23,57
5 | 2022-05-18,54,20,21,55
6 | 2022-05-19,49,23,21,52
7 | 2022-05-20,46,18,22,56
8 | 2022-05-21,51,9,23,58
9 | 2022-05-22,47,9,27,59
10 | 2022-05-23,45,19,21,58
11 | 2022-05-24,49,21,23,53
12 | 2022-05-25,55,21,21,61
13 | 2022-05-26,53,19,22,68
14 | 2022-05-27,52,16,23,52
15 | 2022-05-28,46,8,24,59
16 | 2022-05-29,45,7,22,56
17 | 2022-05-30,45,9,24,61
18 | 2022-05-31,46,19,20,58
19 | 2022-06-01,51,21,22,56
20 | 2022-06-02,47,19,22,54
21 | 2022-06-03,46,17,21,56
22 | 2022-06-04,45,9,23,58
23 | 2022-06-05,47,9,23,60
24 | 2022-06-06,48,20,21,58
25 | 2022-06-07,46,23,21,57
26 | 2022-06-08,48,22,24,56
27 | 2022-06-09,48,20,23,55
28 | 2022-06-10,48,17,22,56
29 | 2022-06-11,47,9,25,54
30 | 2022-06-12,46,9,24,56
31 | 2022-06-13,46,20,22,54
32 | 2022-06-14,46,21,23,58
33 | 2022-06-15,47,21,23,58
34 | 2022-06-16,46,21,24,56
35 | 2022-06-17,51,17,23,57
36 | 2022-06-18,44,9,24,54
37 | 2022-06-19,43,8,26,59
38 | 2022-06-20,45,16,25,53
39 | 2022-06-21,53,23,24,56
40 | 2022-06-22,48,21,25,58
41 | 2022-06-23,48,22,26,55
42 | 2022-06-24,54,18,24,56
43 | 2022-06-25,54,9,25,57
44 | 2022-06-26,48,8,23,62
45 | 2022-06-27,51,19,24,58
46 | 2022-06-28,54,21,25,56
47 | 2022-06-29,49,20,25,61
48 | 2022-06-30,53,19,27,58
49 | 2022-07-01,56,17,26,55
50 | 2022-07-02,47,8,25,56
51 | 2022-07-03,49,8,28,58
52 | 2022-07-04,47,9,25,58
53 | 2022-07-05,52,20,22,61
54 | 2022-07-06,49,20,27,60
55 | 2022-07-07,49,21,26,56
56 | 2022-07-08,58,17,27,53
57 | 2022-07-09,61,10,30,55
58 | 2022-07-10,56,15,31,55
59 | 2022-07-11,59,27,27,55
60 | 2022-07-12,52,25,28,57
61 | 2022-07-13,54,25,26,55
62 | 2022-07-14,61,25,25,60
63 | 2022-07-15,51,22,25,58
64 | 2022-07-16,50,12,24,58
65 | 2022-07-17,53,13,28,57
66 | 2022-07-18,48,24,25,57
67 | 2022-07-19,50,26,25,57
68 | 2022-07-20,47,26,25,53
69 | 2022-07-21,49,24,26,55
70 | 2022-07-22,50,21,26,57
71 | 2022-07-23,47,9,26,55
72 | 2022-07-24,45,8,27,57
73 | 2022-07-25,47,19,26,59
74 | 2022-07-26,47,20,28,58
75 | 2022-07-27,47,20,27,56
76 | 2022-07-28,48,19,26,57
77 | 2022-07-29,47,18,27,58
78 | 2022-07-30,46,9,28,53
79 | 2022-07-31,48,9,27,58
80 | 2022-08-01,48,19,25,56
81 | 2022-08-02,51,21,25,56
82 | 2022-08-03,49,21,27,55
83 | 2022-08-04,47,20,24,54
84 | 2022-08-05,47,17,25,54
85 | 2022-08-06,47,10,26,57
86 | 2022-08-07,46,9,26,58
87 | 2022-08-08,46,19,24,59
88 | 2022-08-09,62,21,25,59
89 | 2022-08-10,52,20,25,56
90 | 2022-08-11,54,21,24,61
91 | 2022-08-12,60,18,24,59
92 | 2022-08-13,55,9,26,56
93 | 2022-08-14,52,9,25,55
94 | 2022-08-15,48,19,24,54
95 | 2022-08-16,49,21,27,55
96 | 2022-08-17,47,20,24,53
97 | 2022-08-18,45,19,24,53
98 | 2022-08-19,47,18,25,53
99 | 2022-08-20,47,9,25,54
100 | 2022-08-21,49,10,27,56
101 | 2022-08-22,44,20,23,55
102 | 2022-08-23,46,22,23,52
103 | 2022-08-24,49,22,23,52
104 | 2022-08-25,47,21,23,56
105 | 2022-08-26,56,17,23,54
106 | 2022-08-27,52,9,25,55
107 | 2022-08-28,51,9,26,57
108 | 2022-08-29,47,19,22,56
109 | 2022-08-30,47,21,22,52
110 | 2022-08-31,46,20,21,52
111 | 2022-09-01,47,18,22,55
112 | 2022-09-02,48,16,21,49
113 | 2022-09-03,50,8,24,52
114 | 2022-09-04,47,8,25,56
115 | 2022-09-05,48,10,27,56
116 | 2022-09-06,45,19,24,54
117 | 2022-09-07,49,20,21,54
118 | 2022-09-08,51,19,23,50
119 | 2022-09-09,49,16,21,52
120 | 2022-09-10,49,9,21,52
121 | 2022-09-11,52,9,22,55
122 | 2022-09-12,49,19,21,50
123 | 2022-09-13,54,21,20,52
124 | 2022-09-14,47,21,20,49
125 | 2022-09-15,45,20,30,51
126 | 2022-09-16,42,16,22,48
127 | 2022-09-17,47,9,23,52
128 | 2022-09-18,50,9,23,51
129 | 2022-09-19,48,20,21,48
130 | 2022-09-20,47,21,22,50
131 | 2022-09-21,47,22,23,51
132 | 2022-09-22,46,21,23,55
133 | 2022-09-23,46,18,21,49
134 | 2022-09-24,47,9,22,50
135 | 2022-09-25,48,9,23,53
136 | 2022-09-26,45,17,23,51
137 | 2022-09-27,46,20,23,47
138 | 2022-09-28,49,19,23,50
139 | 2022-09-29,45,18,30,56
140 | 2022-09-30,49,17,32,56
141 | 2022-10-01,47,9,36,64
142 | 2022-10-02,52,9,39,66
143 | 2022-10-03,53,19,31,60
144 | 2022-10-04,64,21,33,66
145 | 2022-10-05,58,20,34,68
146 | 2022-10-06,49,19,33,62
147 | 2022-10-07,51,17,22,49
148 | 2022-10-08,50,9,24,48
149 | 2022-10-09,52,9,22,52
150 | 2022-10-10,52,16,21,50
151 | 2022-10-11,51,20,22,50
152 | 2022-10-12,47,19,22,49
153 | 2022-10-13,46,20,23,48
154 | 2022-10-14,46,18,20,49
155 | 2022-10-15,49,9,22,48
156 | 2022-10-16,52,9,23,52
157 | 2022-10-17,47,19,22,52
158 | 2022-10-18,48,20,24,49
159 | 2022-10-19,46,19,24,50
160 | 2022-10-20,46,20,22,49
161 | 2022-10-21,49,18,22,47
162 | 2022-10-22,50,9,23,49
163 | 2022-10-23,58,8,22,53
164 | 2022-10-24,53,18,22,50
165 | 2022-10-25,53,21,22,50
166 | 2022-10-26,52,19,22,49
167 | 2022-10-27,56,19,28,49
168 | 2022-10-28,100,17,26,48
169 | 2022-10-29,75,8,25,49
170 | 2022-10-30,66,9,23,56
171 | 2022-10-31,69,17,21,83
172 | 2022-11-01,75,19,21,58
173 | 2022-11-02,64,21,23,51
174 | 2022-11-03,61,19,24,49
175 | 2022-11-04,76,17,28,52
176 | 2022-11-05,69,9,23,54
177 | 2022-11-06,62,8,25,51
178 | 2022-11-07,66,18,23,50
179 | 2022-11-08,60,18,24,47
180 | 2022-11-09,64,18,21,45
181 | 2022-11-10,61,19,22,49
182 | 2022-11-11,69,16,23,47
183 |
--------------------------------------------------------------------------------
/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/Causal Python -- 3 Simple Techniques to Jump-Start Your Causal Inference Journey Today.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "91bf428a",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "import numpy as np\n",
11 | "import pandas as pd\n",
12 | "\n",
13 | "import dowhy\n",
14 | "from dowhy import CausalModel\n",
15 | "\n",
16 | "import matplotlib.pyplot as plt\n",
17 | "plt.style.use('fivethirtyeight')"
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": 2,
23 | "id": "ab111310",
24 | "metadata": {},
25 | "outputs": [
26 | {
27 | "data": {
28 | "text/plain": [
29 | "'0.8'"
30 | ]
31 | },
32 | "execution_count": 2,
33 | "metadata": {},
34 | "output_type": "execute_result"
35 | }
36 | ],
37 | "source": [
38 | "dowhy.__version__"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "id": "e602c0f9",
44 | "metadata": {},
45 | "source": [
46 | "# Causal Python: 3 Simple Techniques to Jump-Start Your Causal Inference Journey Today\n",
47 | "\n",
48 | "In this post we focus on causal inference. For the purpose of this article, we’ll understand causal inference as a process of estimating the causal effect of one variable on another variable from observational data.\n",
49 | "\n",
50 | "\n",
51 | "Check my upcoming [causal book](https://causalpython.io) and be sure to join our growing community at [causalpython.io](https://causalpython.io) for even more tips, tricks, and support. See you there! :) \n",
52 | "\n",
53 | "Now, let's learn something new!\n",
54 | "\n",
55 | " \n",
56 | " "
57 | ]
58 | },
59 | {
60 | "cell_type": "markdown",
61 | "id": "06f2d70a",
62 | "metadata": {},
63 | "source": [
64 | "## Blog post\n",
65 | "\n",
66 | "This code comes with an accompanying [**blog post!**](https://medium.com/towards-data-science/causal-kung-fu-in-python-3-basic-techniques-to-jump-start-your-causal-inference-journey-tonight-ae09181704f7)\n",
67 | "\n",
68 | "Make sure to **check it out**!"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "id": "8356dea8",
74 | "metadata": {},
75 | "source": [
76 | "## Generate the datasets"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": 93,
82 | "id": "4e7ebae2",
83 | "metadata": {},
84 | "outputs": [],
85 | "source": [
86 | "# Define the sample size\n",
87 | "sample_size = 1000\n",
88 | "\n",
89 | "# The first dataset\n",
90 | "W = np.random.randn(sample_size)\n",
91 | "X = 0.3*W + np.random.randn(sample_size)\n",
92 | "Y = 0.45*W + 0.87*X + 0.4*np.random.randn(sample_size)\n",
93 | "\n",
94 | "# Note that we pass all the variables to the dataframe\n",
95 | "data_1 = pd.DataFrame(np.vstack([X, W, Y]).T, columns=['X', 'W', 'Y'])\n",
96 | "\n",
97 | "\n",
98 | "# The second dataset\n",
99 | "U = np.random.randn(sample_size)\n",
100 | "X = 0.3*U + np.random.randn(sample_size)\n",
101 | "Z = 0.78*X + 0.3*np.random.randn(sample_size)\n",
102 | "Y = 0.45*U + 0.87*Z + 0.4*np.random.randn(sample_size)\n",
103 | "\n",
104 | "# Note that we're ommiting U in the dataframe as it's unobserved\n",
105 | "data_2 = pd.DataFrame(np.vstack([X, Z, Y]).T, columns=['X', 'Z', 'Y']) \n",
106 | "\n",
107 | "\n",
108 | "# The third dataset\n",
109 | "U = np.random.randn(sample_size)\n",
110 | "Z = np.random.randn(sample_size)\n",
111 | "X = 0.6*Z + 0.43*U + np.random.randn(sample_size)\n",
112 | "Y = 0.45*U + 0.87*X + 0.4*np.random.randn(sample_size)\n",
113 | "\n",
114 | "# Note that we're ommiting U in the dataframe as it's unobserved\n",
115 | "data_3 = pd.DataFrame(np.vstack([X, Z, Y]).T, columns=['X', 'Z', 'Y'])"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "id": "a81f74d1",
121 | "metadata": {},
122 | "source": [
123 | "## Dataset 1"
124 | ]
125 | },
126 | {
127 | "cell_type": "markdown",
128 | "id": "206ab29d",
129 | "metadata": {},
130 | "source": [
131 | "### Step 1: Model the problem"
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": 94,
137 | "id": "43ce078c",
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "# Create the graph\n",
142 | "graph_1 = \"\"\"\n",
143 | " graph [\n",
144 | " \n",
145 | " directed 1\n",
146 | "\n",
147 | " node [id \"X\" label \"X\"] \n",
148 | " node [id \"W\" label \"W\"]\n",
149 | " node [id \"Y\" label \"Y\"]\n",
150 | " \n",
151 | " edge [source \"W\" target \"X\"]\n",
152 | " edge [source \"W\" target \"Y\"]\n",
153 | " edge [source \"X\" target \"Y\"]\n",
154 | " ] \n",
155 | "\"\"\""
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": 95,
161 | "id": "d0e230ea",
162 | "metadata": {},
163 | "outputs": [],
164 | "source": [
165 | "# Instantiate the CausalModel object\n",
166 | "model_1 = CausalModel(\n",
167 | " data=data_1,\n",
168 | " treatment='X',\n",
169 | " outcome='Y',\n",
170 | " graph=graph_1\n",
171 | ")"
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": 96,
177 | "id": "898d8cb0",
178 | "metadata": {},
179 | "outputs": [
180 | {
181 | "data": {
182 | "image/png": "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WW3YsEGZmZnq3Lmz3nzzTXXs2FHdunXTSy+9pK1bt9o9FQCACoFgBsJYVFSUevbsqVdffVU+n08ffvihzj33XD399NNq27at+vXrp7ffflt79+61eyoAACGLYAYiRGxsrAYNGqT3339fPp9Pb7zxhqpUqaIHH3xQLVq0kNfr1aeffqqDBw/aPRUAgJBCMAMRqFq1arryyiuVnp6uTZs26dlnn9WRI0c0YsQIud1u3XDDDfriiy9UWFho91QAAGxHMAMRrlatWqV3nNeuXauHH35Ymzdv1nXXXSe326077rhDc+fOVUlJid1TAQCwBcEMoFSjRo101113acGCBVq+fLluv/12LV26VKmpqWrZsqXuv/9+LV++XJbFcR0AQOTgrByAoCzL0urVq5Wenq7MzEzt3LlTjRs3Lj1T17p1a7snAgBOE2flgiOYAZwwv9+vxYsXyzAMZWdnKz8/Xy1btpTX65XH41HTpk3tnggAOAUEc3A8kgHghLlcrtI7zj6fT59++qlat26t559/Xu3bt9dll12mN954Q7t377Z7KgAAZYZgBnBKKlWqVHrHOTc3V2+//bZq1aqlv/3tb2rZsqVSUlL04YcfKj8/3+6pAACcFoIZwGmrXLmyvF6vPvvsM/l8Po0fP15+v1933XWX3G63rrnmGmVmZuro0aN2TwUA4KQRzADK1FlnnVV6x3n9+vV69NFHtXPnTt10001yu9269dZbNWvWLBUXF9s9FQCAE0IwAyg39evX18iRIzVnzhx98803uuuuu7R69WpdccUVatGihe655x4tWrRIgUDA7qkAAPwprmQAOKMsy9LatWtlmqZM09S2bdvUoEEDpaWlyePxqF27dnI4HHbPBICIwpWM4AhmALYJBAJatmyZTNNUZmam8vLylJiYWHrjuXnz5nZPBICIQDAHxyMZAGzjdDrVuXNnPffcc8rJyZFhGLrwwgv1yiuv6KKLLlKPHj00YcIEbd++3e6pAIAIRjADCAnR0dGld5xzc3M1adIkNWzYUE888YTatGmjAQMG6P3339e+ffvsngoAiDAEM4CQExcXpyFDhuijjz5Sbm6uXnnlFVWqVEn33HOP3G63hg0bpvT0dB0+fNjuqQCACEAwAwhp1atXL73jnJOTo7///e/av3+/hg8fLrfbrZtvvlnTpk1TUVGR3VMBAGGKYAZQYdSpU6f0jvPq1at13333KScnR1dffbXcbrdGjRql+fPny+/32z0VABBGuJIBoMLbsGGDTNOUYRj66aefVLduXaWmpsrr9eqCCy7gTB0A/A9cyQiOYAYQNizL0sqVK2UYhjIzM7Vnzx41a9as9ExdUlKS3RMBICQRzMHxSAaAsOFwONShQwf94x//0MaNG5WVlaWuXbtq4sSJ6tSpky655BK9+OKL+vnnn+2eCgCoQAhmAGHJ5XKpR48eeuWVV5Sbm6uPPvpIiYmJeuaZZ3Teeeepb9++euutt/TLL7/YPRUAEOIIZgBhLyYmRgMHDtR7772n3Nxcvfnmm6pWrZoeeughJSUlyePx6JNPPtHBgwftngoACEEEM4CIUrVqVQ0bNkz//Oc/5fP5NG7cOBUUFOiOO+5QYmKirr/+emVnZ6uwsNDuqQCAEEEwA4hYNWvWLL3jvG7dOo0dO1ZbtmzRDTfcILfbrREjRmj27NkqKSmxeyoAwEYEMwBIatiwoUaNGqWvv/5aK1as0IgRI7R8+XJ5PB61bNlS999/v5YtWybL4rAQAEQazsoBwJ+wLEtr1qxRenq6MjMztWPHDjVq1Kj0TF3r1q258QwgLHBWLjiCGQBOQCAQ0OLFi2UYhrKzs7V//34lJSXJ6/XK6/WqadOmdk8EgFNGMAfHIxkAcAKcTmfpHedNmzbps88+U9u2bTV+/Hi1b99evXv31uuvv65du3bZPRUAUMYIZgA4SZUqVSq94+zz+fTuu++qTp06euSRR9SqVSsNHjxYH3zwgfLz8+2eCgAoAwQzAJyGypUrKy0tTZ9++qlyc3P14osvSpLuvvtuJSYm6uqrr1ZGRoaOHj1q71AAwCkjmAGgjCQkJOj666/X5MmTtWHDBj322GPavXu3br75ZiUmJurWW2/VzJkzVVxcbPdUAMBJIJgBoBzUq1dPd9xxh2bPnq1Vq1Zp9OjRWrNmjYYNGya3260xY8Zo4cKFCgQCdk8FAPwPXMkAgDPEsiytW7dOpmnKMAxt27ZN9evXV1pamrxer9q1a8eZOgC24EpGcAQzANggEAho+fLlMk1TmZmZ2rt3r5o3b1564zkxMdHuiQAiCMEcHI9kAIANnE6nOnXqpHHjxiknJ0emaapDhw567bXX1KFDB3Xv3l0TJkzQ9u3b7Z4KABGPYAYAm0VFRZXecfb5fJo0aZIaN26sJ598Um3atFH//v313nvvad++fXZPBYCIRDADQAiJi4vTkCFD9OGHH8rn8+nVV19VbGys7rvvPrndbl1xxRX6/PPPdejQIbunAkDEIJgBIERVr1699I5zTk6Onn76aR04cEC33Xab3G63brrpJk2dOlVFRUV2TwWAsEYwA0AFULt2bQ0fPlwzZ87UmjVr9MADD8jn8+maa66R2+3WnXfeqfnz58vv99s9FQDCDsEMABVMkyZNNGbMGC1atEhLlizR8OHDtXDhQg0ZMkStWrXSQw89pJUrV8qyOIIEAGWBs3IAEAYsy9I333wjwzCUmZmp3bt3q2nTpvJ6vfJ4PGrZsqXdEwGEMM7KBcdXmAEgDDgcDl100UV65plntGHDBmVnZ6tbt25666231LlzZ3Xp0kXjx4/XTz/9ZPdUAKhwCGYACDMul6v0jrPP59PHH3+spKQkPfvss2rXrp369OmjiRMnas+ePXZPBYAKgWAGgDAWExOjAQMG6N1331Vubq4mTpyohIQEPfzww0pKSlJaWpo+/vhjHThwwO6pABCyCGYAiBBVqlQpveO8adMmPf/88yoqKtKdd94pt9ut6667TtnZ2SooKLB7KgCEFIIZACJQzZo1S+84r1u3TmPHjtXWrVt1ww03yO126/bbb9dXX32lkpISu6cCgO0IZgCIcA0aNNCoUaM0b948rVy5UiNHjtTKlSvl9XqVlJSk++67T0uXLlUgELB7KgDYgrNyAIDfsSxLa9askWmaMk1TO3bsUMOGDeXxeOT1etWmTRs5HA67ZwIoI5yVC45gBgAEFQgEtGTJEhmGoezsbO3bt08tWrQojedzzjnH7okAThPBHByPZAAAgnI6neratavGjx+vTZs26Z///KfatWunl156SRdccIF69eqlV199VTt37rR7KgCUC4IZAHDCoqOjlZycrIkTJyo3N1fvvfee6tatq8cee0ytWrXSoEGD9MEHHyg/P9/uqQBQZghmAMApiY+PV2pqqj755BP5fD699NJLcjqdGj16tBITE3XllVfKNE0dOXLE7qkAcFoIZgDAaUtISND111+v7OxsbdiwQY8//rj27t2rv/zlL3K73Ro+fLhmzJihY8eO2T0VAE4awQwAKFN169bViBEj9NVXX+nbb7/VmDFjtHbtWl155ZVq0aKFRo8erQULFnCmDkCFwZUMAEC5syxL69evl2maMgxDW7duVb169ZSWliav16v27dtzpg6wEVcygiOYAQBnlGVZWr58uQzDUGZmpvbu3atzzz239Eyd2+22eyIQcQjm4HgkAwBwRjkcDnXs2FHjxo1TTk6OMjIy1LFjR73xxhu6+OKLdemll+rll1/Wtm3b7J4KAJIIZgCAjaKiotSrVy+99tpr8vl8+uCDD9SsWTM99dRTatOmjfr166d33nlHeXl5dk8FEMEIZgBASIiNjdXgwYM1adIk5ebm6vXXX1d8fLweeOABtWjRQkOHDtVnn32mQ4cO2T0VQIQhmAEAIadatWq66qqrZJqmcnJy9Mwzz+jQoUO6/fbblZiYqBtvvFFTpkxRUVGR3VMBRACCGQAQ0mrXrq1bbrlFM2bM0HfffaeHHnpI33//va699lolJiZq5MiRmjdvnvx+v91TAYQpghkAUGE0btxYo0eP1sKFC7V06VLdeuutWrx4sVJSUtSyZUs98MADWrFihSyLA1AAyg5n5QAAFZplWfr222+Vnp6uzMxM7dq1S02aNCk9U9eqVSu7JwIhj7NywRHMAICw4ff7tWjRIhmGoezsbB04cECtWrWS1+tVWlqamjZtavdEICQRzMHxSAYAIGy4XK7SO84+n0+ffPKJWrZsqeeee07t27dXcnKy3nzzTe3Zs8fuqQAqEIIZABCWYmJi1L9/f73zzjvy+Xx66623dNZZZ+n//u//lJSUpNTUVH300UfKz8+3eyqAEEcwAwDCXpUqVTR06FB9/vnn8vl8euGFF1RcXKxRo0bJ7Xbr2muvVVZWlgoKCuyeCiAEEcwAgIhSo0aN0jvO69ev1yOPPKLt27frxhtvVGJiom677TZ9+eWXKi4utnsqgBBBMAMAIlb9+vV15513au7cuVq5cqVGjRqlVatWaejQoUpKStK9996rxYsXKxAI2D0VgI24kgEAwH+wLEvfffedTNOUaZravn27GjZsqLS0NHm9XrVt21YOh8PumUCZ4kpGcAQzAAB/IhAIaOnSpTIMQ1lZWdq3b5/cbnfpjedzzz3X7olAmSCYg+ORDAAA/oTT6VSXLl30wgsvaNOmTUpPT1f79u01YcIEXXjhherZs6deeeUV7dixw+6pAMoRwQwAwAmIjo7W5ZdfrokTJ8rn8+n9999X/fr19fjjj6t169YaOHCgJk2apP3799s9FUAZI5gBADhJ8fHxSklJ0ccffyyfz6eXX35ZUVFRGjNmjNxut4YNGybDMHTkyBG7pwIoAwQzAACnISEhQdddd52ysrK0ceNGPfHEE9q3b59uueUWJSYm6i9/+YumT5+uY8eO2T0VwCkimAEAKCNnn322br/9dn355ZdavXq17r33Xm3cuFFXXXWV3G637r77bn399dfy+/12TwVwEriSAQBAOVu/fr1M05RhGPr5559Vt27d0jN1559/PmfqYDuuZARHMAMAcIZYlqUVK1bIMAxlZmbql19+0TnnnFN6pq5FixZ2T0SEIpiD45EMAADOEIfDoYsvvljPPvusNm7cqMzMTHXu3FlvvvmmOnbsqG7duumll17S1q1b7Z4K4D8QzAAA2CAqKko9e/bUq6++Kp/Ppw8//FDnnnuunn76abVt21b9+vXT22+/rb1799o9FYh4BDMAADaLjY3VoEGD9P7778vn8+mNN95QlSpV9OCDD6pFixbyer369NNPdfDgQbunAhGJYAYAIIRUq1ZNV155pdLT07Vp0yY9++yzOnLkiEaMGCG3260bbrhBX3zxhQoLC+2eCkQMghkAgBBVq1at0jvOa9eu1cMPP6zNmzfruuuuk9vt1h133KE5c+aopKTE7qlAWCOYAQCoABo1aqS77rpLCxYs0LJly3Tbbbdp6dKlSktLU8uWLXX//fdr+fLlsiyOXwFljbNyAABUUJZlafXq1UpPT1dmZqZ27typxo0bl56pa926td0TUUFwVi44ghkAgDDg9/u1ePFiGYah7Oxs5efnq2XLlvJ6vfJ4PGratKndExHCCObgeCQDAIAw4HK5Su84+3w+ffrpp2rdurWef/55tW/fXpdddpneeOMN7d692+6pQIVDMAMAEGYqVapUesc5NzdXb7/9tmrVqqW//e1vatmypYYMGaIPP/xQ+fn5dk8FKgSCGQCAMFa5cmV5vV599tln8vl8Gj9+vAKBgO666y653W5dc801yszM1NGjR+2eCoQsghkAgAhx1llnld5xXr9+vR599FHt3LlTN910k9xut2699VbNmjVLxcXFdk8FQgrBDABABKpfv75GjhypOXPm6JtvvtFdd92l1atX64orrlCLFi10zz33aNGiRQoEAnZPBWzHlQwAACDp+Jm6tWvXyjRNmaapbdu2qUGDBkpLS5PH41G7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\n",
183 | "text/plain": [
184 | ""
185 | ]
186 | },
187 | "metadata": {},
188 | "output_type": "display_data"
189 | }
190 | ],
191 | "source": [
192 | "model_1.view_model()"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "id": "97025bb1",
198 | "metadata": {},
199 | "source": [
200 | "### Step 2: Identify the estimand"
201 | ]
202 | },
203 | {
204 | "cell_type": "code",
205 | "execution_count": 97,
206 | "id": "cfc7e756",
207 | "metadata": {},
208 | "outputs": [
209 | {
210 | "name": "stdout",
211 | "output_type": "stream",
212 | "text": [
213 | "MODEL 2:\n",
214 | "Estimand type: nonparametric-ate\n",
215 | "\n",
216 | "### Estimand : 1\n",
217 | "Estimand name: backdoor\n",
218 | "Estimand expression:\n",
219 | " d \n",
220 | "────(E[Y|W])\n",
221 | "d[X] \n",
222 | "Estimand assumption 1, Unconfoundedness: If U→{X} and U→Y then P(Y|X,W,U) = P(Y|X,W)\n",
223 | "\n",
224 | "### Estimand : 2\n",
225 | "Estimand name: iv\n",
226 | "No such variable(s) found!\n",
227 | "\n",
228 | "### Estimand : 3\n",
229 | "Estimand name: frontdoor\n",
230 | "No such variable(s) found!\n",
231 | "\n"
232 | ]
233 | }
234 | ],
235 | "source": [
236 | "estimand_1 = model_1.identify_effect()\n",
237 | "print(f'MODEL 2:\\n{estimand_1}')"
238 | ]
239 | },
240 | {
241 | "cell_type": "markdown",
242 | "id": "eb32ab12",
243 | "metadata": {},
244 | "source": [
245 | "### Step 3: Estimate the effect"
246 | ]
247 | },
248 | {
249 | "cell_type": "code",
250 | "execution_count": 98,
251 | "id": "84855daa",
252 | "metadata": {
253 | "scrolled": true
254 | },
255 | "outputs": [
256 | {
257 | "name": "stdout",
258 | "output_type": "stream",
259 | "text": [
260 | "linear_regression\n",
261 | "{'control_value': 0, 'treatment_value': 1, 'test_significance': None, 'evaluate_effect_strength': False, 'confidence_intervals': False, 'target_units': 'ate', 'effect_modifiers': []}\n",
262 | "Estimate of causal effect (MODEL 1): 0.8591823936185948\n"
263 | ]
264 | }
265 | ],
266 | "source": [
267 | "estimate_1 = model_1.estimate_effect(\n",
268 | " identified_estimand=estimand_1,\n",
269 | " method_name='backdoor.linear_regression')\n",
270 | "\n",
271 | "print(f'Estimate of causal effect (MODEL 1): {estimate_1.value}')"
272 | ]
273 | },
274 | {
275 | "cell_type": "markdown",
276 | "id": "1a93cc78",
277 | "metadata": {},
278 | "source": [
279 | "## Dataset 2"
280 | ]
281 | },
282 | {
283 | "cell_type": "code",
284 | "execution_count": 99,
285 | "id": "beb91e00",
286 | "metadata": {},
287 | "outputs": [
288 | {
289 | "name": "stdout",
290 | "output_type": "stream",
291 | "text": [
292 | "\n",
293 | "Graph for Model 2:\n",
294 | "graph [directed 1\n",
295 | "\tnode [id \"X\" label \"X\"]\n",
296 | "\tnode [id \"Y\" label \"Y\"]\n",
297 | "\tnode [id \"Z\" label \"Z\"]\n",
298 | "\tnode [id \"U\" label \"U\"]\n",
299 | "\tedge [source \"X\" target \"Z\"]\n",
300 | "\tedge [source \"Z\" target \"Y\"]\n",
301 | "\tedge [source \"U\" target \"X\"]\n",
302 | "\tedge [source \"U\" target \"Y\"]\n",
303 | "]\n",
304 | "\n",
305 | "Estimand for Model 2:\n",
306 | "Estimand type: nonparametric-ate\n",
307 | "\n",
308 | "### Estimand : 1\n",
309 | "Estimand name: backdoor\n",
310 | "No such variable(s) found!\n",
311 | "\n",
312 | "### Estimand : 2\n",
313 | "Estimand name: iv\n",
314 | "No such variable(s) found!\n",
315 | "\n",
316 | "### Estimand : 3\n",
317 | "Estimand name: frontdoor\n",
318 | "Estimand expression:\n",
319 | " ⎡ d d ⎤\n",
320 | "E⎢────(Y)⋅────([Z])⎥\n",
321 | " ⎣d[Z] d[X] ⎦\n",
322 | "Estimand assumption 1, Full-mediation: Z intercepts (blocks) all directed paths from X to Y.\n",
323 | "Estimand assumption 2, First-stage-unconfoundedness: If U→{X} and U→{Z} then P(Z|X,U) = P(Z|X)\n",
324 | "Estimand assumption 3, Second-stage-unconfoundedness: If U→{Z} and U→Y then P(Y|Z, X, U) = P(Y|Z, X)\n",
325 | "\n",
326 | "two_stage_regression\n",
327 | "{'control_value': 0, 'treatment_value': 1, 'test_significance': None, 'evaluate_effect_strength': False, 'confidence_intervals': False, 'target_units': 'ate', 'effect_modifiers': [], 'first_stage_model': None, 'second_stage_model': None}\n",
328 | "{'control_value': 0, 'treatment_value': 1, 'test_significance': None, 'evaluate_effect_strength': False, 'confidence_intervals': False, 'target_units': 'ate', 'effect_modifiers': [], 'first_stage_model': None, 'second_stage_model': None}\n",
329 | "\n",
330 | "\n",
331 | "Estimate of causal effect (Model 2): 0.662280116061264\n"
332 | ]
333 | },
334 | {
335 | "data": {
336 | "image/png": "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\n",
337 | "text/plain": [
338 | ""
339 | ]
340 | },
341 | "metadata": {},
342 | "output_type": "display_data"
343 | }
344 | ],
345 | "source": [
346 | "# STEP 1\n",
347 | "\n",
348 | "nodes_2 = ['X', 'Y', 'Z', 'U']\n",
349 | "edges_2 = ['XZ', 'ZY', 'UX', 'UY']\n",
350 | "\n",
351 | "# Generate the GML graph\n",
352 | "graph_2 = 'graph [directed 1\\n'\n",
353 | "\n",
354 | "for node in nodes_2:\n",
355 | " graph_2 += f'\\tnode [id \"{node}\" label \"{node}\"]\\n'\n",
356 | "\n",
357 | "for edge in edges_2:\n",
358 | " graph_2 += f'\\tedge [source \"{edge[0]}\" target \"{edge[1]}\"]\\n'\n",
359 | " \n",
360 | "graph_2 += ']'\n",
361 | "\n",
362 | "# Print out the graph\n",
363 | "print(f'\\nGraph for Model 2:\\n{graph_2}\\n')\n",
364 | "\n",
365 | "# Instantiate the model\n",
366 | "model_2 = CausalModel(\n",
367 | " data=data_2,\n",
368 | " treatment='X',\n",
369 | " outcome='Y',\n",
370 | " graph=graph_2, \n",
371 | ")\n",
372 | "\n",
373 | "model_2.view_model()\n",
374 | "\n",
375 | "\n",
376 | "# STEP 2\n",
377 | "estimand_2 = model_2.identify_effect()\n",
378 | "print(f'Estimand for Model 2:\\n{estimand_2}')\n",
379 | "\n",
380 | "\n",
381 | "# STEP 3\n",
382 | "estimate_2 = model_2.estimate_effect(\n",
383 | " identified_estimand=estimand_2,\n",
384 | " method_name='frontdoor.two_stage_regression')\n",
385 | "\n",
386 | "print(f'\\n\\nEstimate of causal effect (Model 2): {estimate_2.value}')"
387 | ]
388 | },
389 | {
390 | "cell_type": "markdown",
391 | "id": "b503c722",
392 | "metadata": {},
393 | "source": [
394 | "## Dataset 3 - Instrumental Variable"
395 | ]
396 | },
397 | {
398 | "cell_type": "code",
399 | "execution_count": 100,
400 | "id": "bb7c2802",
401 | "metadata": {
402 | "scrolled": true
403 | },
404 | "outputs": [
405 | {
406 | "name": "stdout",
407 | "output_type": "stream",
408 | "text": [
409 | "\n",
410 | "Graph for Model 3:\n",
411 | "graph [directed 1\n",
412 | "\tnode [id \"X\" label \"X\"]\n",
413 | "\tnode [id \"Y\" label \"Y\"]\n",
414 | "\tnode [id \"Z\" label \"Z\"]\n",
415 | "\tnode [id \"U\" label \"U\"]\n",
416 | "\tedge [source \"Z\" target \"X\"]\n",
417 | "\tedge [source \"U\" target \"X\"]\n",
418 | "\tedge [source \"U\" target \"Y\"]\n",
419 | "\tedge [source \"X\" target \"Y\"]\n",
420 | "]\n",
421 | "\n",
422 | "Estimand for Model 2:\n",
423 | "Estimand type: nonparametric-ate\n",
424 | "\n",
425 | "### Estimand : 1\n",
426 | "Estimand name: backdoor\n",
427 | "No such variable(s) found!\n",
428 | "\n",
429 | "### Estimand : 2\n",
430 | "Estimand name: iv\n",
431 | "Estimand expression:\n",
432 | " ⎡ -1⎤\n",
433 | " ⎢ d ⎛ d ⎞ ⎥\n",
434 | "E⎢────(Y)⋅⎜────([X])⎟ ⎥\n",
435 | " ⎣d[Z] ⎝d[Z] ⎠ ⎦\n",
436 | "Estimand assumption 1, As-if-random: If U→→Y then ¬(U →→{Z})\n",
437 | "Estimand assumption 2, Exclusion: If we remove {Z}→{X}, then ¬({Z}→Y)\n",
438 | "\n",
439 | "### Estimand : 3\n",
440 | "Estimand name: frontdoor\n",
441 | "No such variable(s) found!\n",
442 | "\n",
443 | "instrumental_variable\n",
444 | "\n",
445 | "\n",
446 | "Estimate of causal effect (Model 3): 0.9033910718128734\n"
447 | ]
448 | },
449 | {
450 | "data": {
451 | "image/png": "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LlVmZqZq1KihF154QdHR0X5br379+vruu+8u+L3f/OY3+ve//+23tQEAl8aEGQB+xTAM9ezZUzk5OapTp47atWuniRMnqri42C/rNW3aVIZhXDBH06ZN/bImAKDsmDADgBdut1uzZs3SX//6V91+++2aP3++7rjjDp+usW/fPt1///3n7dBhGIb++c9/6ve//71P1wMAXB4mzADghd1u16OPPqotW7aouLhYzZs319y5c+XxeHy2xm233aaHH35Ydrv9F+t27tyZsgwAAYAJMwCUkcvl0pNPPql58+YpLi5Oc+bMUZ06dXxy7l9PmZkuA0DgYMIMAGUUFRWlqVOn6s0339Snn36q6OhorV69+rzjTNPU999/f1nn/s+U2TAMGYbBdBkAAgiFGQAuU1xcnJxOp+Li4jRgwAA5HA4VFBT89P0nnnhCd9xxh7788svLOu/o0aNlmqZM09To0aN9nBoAUF5ckgEA5WSaplasWKGRI0eqevXqmjt3rkzTVMeOHWWaprp166YXX3zxkuex2fbJbs9XWNh2/e//viObTapbt548nltVWhont7uxPJ7bKuARAQAuhMIMAFfo0KFDGjJkiHJzc1W1alUVFhbK4/HIMAzt2LFDf/rTny5wL5fCw19TRMR8GcaJH7/OfyOhadpkmjVlmtepqGiwSkp6Sory+2MCAPwfCjMA+IDH41FsbKz27t370212u10xMTFas2bNL4612/MUFTVUNtthGUZRmdcwzQh5PDfK5Zojt7uJz7IDALzjGmYA8IG33nrrF2VZOreH844dO5SVlfXjLR5FRg5X1ap9ZLd/cVllWZIMo0h2+xeqWrWPIiOHS/Ld1nYAgItjwgwAV+jUqVO64447dPr06Qt+v379+vrnP/NVrVo/hYVtk8129orX9HiqqrT0Ablci8XsAwD8i2dZALhCNptNDz30kH7/+9+rSpUq533/s88+0/79LX1Wls+teVZhYdsUGZnhk/MBAC6OCTMA+JDH49Hhw4e1f/9+/e///q8++ugjnTz5Dy1ceEpVq164LLdvL+XmSnv3Sr/97S+/d+KE9Mc/SnXrSjk5ku1XYw6Pp6bOnl3MNc0A4EcUZgDwK5euuqqx7PYvLnrEkSPSnXdKd98tbdr0y+8lJkpr10rvvy/Vq3fh+7vdt+j06XyxewYA+AeXZACAH4WHvyab7bDXY2rXlp5/Xtq8WZo37/9uf/NNadkyadq0i5dlSbLZjig8fKmPEgMAfo0JMwD40VVX3Su7/d9lOvaRR6R1685dmnH11dIf/iA1aHCuSF+K232bTp/+5xWmBQBcCIUZAPzEZtunatXayWY7VqbjT5w4d2nG7bdLtWqdK8p790o33XTp+3o8tXTmzDo+ERAA/CDM6gAAEKzs9nwZxvEyH3/dddJLL0lt257786uvlq0sS5JhHJfdvpPCDAB+wDXMAOAnYWHbZRiX9yLegw9K998v1a8v9epV9vsZhkdhYdsvMyEAoCwozADgJzbbgXLdLyJCusB2zn5bDwDgHYUZAPymoj+62l3B6wFAaKAwA4DfVPRTrL2C1wOA0EBhBgA/8XhuDer1ACBUUJgBwE9KS+NkmkaFrGWaNpWWxlXIWgAQathWDgD8xO1uLNOsKcMo2z7M/5GVdflrmWZNud33X/4dAQCXxIQZAPzE47lNpnldhaxlmtexBzMA+AmFGQD8qKjIIdOM8OsaphmpoiKHX9cAgFBGYQYAPyop6SmP50a/ruHx1FZJSaJf1wCAUEZhBgC/ipLLNUceT02/nN3jqSmX63lJUX45PwCAwgwAfud2N1FJSXt5PFV9el6Pp6pKSh6S2x3t0/MCAH6JwgwAFaCwcIZKSx/wWWn2eKqqtPQBFRY+65PzAQAujsIMABXCJpdrsUpKul/x5RkeT02VlPSQy7VYPI0DgP8ZBQUFptUhACCU2O15iooaKpvtiAyjsMz3M81IeTy15XLNkdvdxI8JAQA/R2EGAEu4FB6+VBER82UYJ2QYx2UYnvOOMk2bTLOmTLOmiooG/7gbBm/wA4CKRGEGAIvZbPtkt+9UWNh22WwHJLkl2eXx3KrS0ji53ffzoSQAYCEKMwAAAOAF7xYBAAAAvKAwAwAAAF5QmAEAAAAvKMwAAACAFxRmAAAAwAsKMwAAAOAFhRkAAADwgsIMAAAAeEFhBgAAALygMAMAAABeUJgBAAAALyjMAAAAgBcUZgAAAMALCjMAAADgBYUZAAAA8ILCDAAAAHhBYQYAAAC8oDADAAAAXlCYAQAAAC8ozAAAAIAXFGYAAADACwozAAAA4AWFGQAAAPCCwgwAAAB4QWEGAAAAvKAwAwAAAF5QmAEAAAAvKMwAAACAFxRmAAAAwAsKMwAAAOAFhRkAAADwgsIMAAAAeEFhBgAAALygMAMAAABeUJgBAAAALyjMAAAAgBf/H2kuYE8vfFQXAAAAAElFTkSuQmCC\n",
452 | "text/plain": [
453 | ""
454 | ]
455 | },
456 | "metadata": {},
457 | "output_type": "display_data"
458 | }
459 | ],
460 | "source": [
461 | "# STEP 1\n",
462 | "\n",
463 | "nodes_3 = ['X', 'Y', 'Z', 'U']\n",
464 | "edges_3 = ['ZX', 'UX', 'UY', 'XY']\n",
465 | "\n",
466 | "# Generate the GML graph\n",
467 | "graph_3 = 'graph [directed 1\\n'\n",
468 | "\n",
469 | "for node in nodes_3:\n",
470 | " graph_3 += f'\\tnode [id \"{node}\" label \"{node}\"]\\n'\n",
471 | "\n",
472 | "for edge in edges_3:\n",
473 | " graph_3 += f'\\tedge [source \"{edge[0]}\" target \"{edge[1]}\"]\\n'\n",
474 | " \n",
475 | "graph_3 += ']'\n",
476 | "\n",
477 | "# Print out the graph\n",
478 | "print(f'\\nGraph for Model 3:\\n{graph_3}\\n')\n",
479 | "\n",
480 | "# Instantiate the model\n",
481 | "model_3 = CausalModel(\n",
482 | " data=data_3,\n",
483 | " treatment='X',\n",
484 | " outcome='Y',\n",
485 | " graph=graph_3, \n",
486 | ")\n",
487 | "\n",
488 | "model_3.view_model()\n",
489 | "\n",
490 | "\n",
491 | "# STEP 2\n",
492 | "estimand_3 = model_3.identify_effect()\n",
493 | "print(f'Estimand for Model 2:\\n{estimand_3}')\n",
494 | "\n",
495 | "\n",
496 | "# STEP 3\n",
497 | "estimate_3 = model_3.estimate_effect(\n",
498 | " identified_estimand=estimand_3,\n",
499 | " method_name='iv.instrumental_variable')\n",
500 | "\n",
501 | "print(f'\\n\\nEstimate of causal effect (Model 3): {estimate_3.value}')"
502 | ]
503 | },
504 | {
505 | "cell_type": "code",
506 | "execution_count": null,
507 | "id": "494c5d13",
508 | "metadata": {},
509 | "outputs": [],
510 | "source": [
511 | "# Estimand for Model 2:\n",
512 | "# Estimand type: nonparametric-ate\n",
513 | "\n",
514 | "# ### Estimand : 1\n",
515 | "# Estimand name: backdoor\n",
516 | "# No such variable(s) found!\n",
517 | "\n",
518 | "# ### Estimand : 2\n",
519 | "# Estimand name: iv\n",
520 | "# Estimand expression:\n",
521 | "# ⎡ -1⎤\n",
522 | "# ⎢ d ⎛ d ⎞ ⎥\n",
523 | "# E⎢────(Y)⋅⎜────([X])⎟ ⎥\n",
524 | "# ⎣d[Z] ⎝d[Z] ⎠ ⎦\n",
525 | "# Estimand assumption 1, As-if-random: If U→→Y then ¬(U →→{Z})\n",
526 | "# Estimand assumption 2, Exclusion: If we remove {Z}→{X}, then ¬({Z}→Y)\n",
527 | "\n",
528 | "# ### Estimand : 3\n",
529 | "# Estimand name: frontdoor\n",
530 | "# No such variable(s) found!"
531 | ]
532 | }
533 | ],
534 | "metadata": {
535 | "kernelspec": {
536 | "display_name": "Python [conda env:causal_kung_fu_1_py38]",
537 | "language": "python",
538 | "name": "conda-env-causal_kung_fu_1_py38-py"
539 | },
540 | "language_info": {
541 | "codemirror_mode": {
542 | "name": "ipython",
543 | "version": 3
544 | },
545 | "file_extension": ".py",
546 | "mimetype": "text/x-python",
547 | "name": "python",
548 | "nbconvert_exporter": "python",
549 | "pygments_lexer": "ipython3",
550 | "version": "3.8.13"
551 | }
552 | },
553 | "nbformat": 4,
554 | "nbformat_minor": 5
555 | }
556 |
--------------------------------------------------------------------------------
/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/causal-kung-fu-01.yml:
--------------------------------------------------------------------------------
1 | name: causal_kung_fu_1_py38
2 | channels:
3 | - defaults
4 | - conda-forge
5 | dependencies:
6 | - pip=21.0.1
7 | - python=3.8
8 | - nb_conda=2.2.1
9 | - numpy=1.20.3
10 | - pandas=1.3.1
11 | - matplotlib=3.5.0
12 | - seaborn=0.11.2
13 | - pip:
14 | - econml==0.12
15 | - dowhy==0.8
16 | - graphviz==0.20
17 |
--------------------------------------------------------------------------------
/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/causal_model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/causal_model.png
--------------------------------------------------------------------------------
/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/img/CausalPython.io__flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today/img/CausalPython.io__flat.png
--------------------------------------------------------------------------------
/Jane the Discoverer/Graph Plotting.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "35d4da80",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "import graphviz"
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": 9,
16 | "id": "75e54c14",
17 | "metadata": {},
18 | "outputs": [],
19 | "source": [
20 | "graph = graphviz.Digraph(format='png')\n",
21 | "\n",
22 | "# Add nodes\n",
23 | "nodes = ['altitude', 'oxygen_density', 'temperature', 'risk_of_death', 'mehendretex']\n",
24 | "[graph.node(n) for n in nodes]\n",
25 | "\n",
26 | "graph.edges(\n",
27 | " [\n",
28 | " ('altitude', 'temperature'),\n",
29 | " ('altitude', 'oxygen_density'),\n",
30 | " ('temperature', 'oxygen_density'),\n",
31 | " ('temperature', 'risk_of_death'),\n",
32 | " ('altitude', 'risk_of_death'),\n",
33 | " ('oxygen_density', 'risk_of_death'),\n",
34 | " ('mehendretex', 'risk_of_death')\n",
35 | " ]\n",
36 | ")"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": 10,
42 | "id": "98c10eb3",
43 | "metadata": {},
44 | "outputs": [
45 | {
46 | "data": {
47 | "image/svg+xml": [
48 | "\n",
49 | "\n",
51 | "\n",
53 | "\n",
54 | "\n",
56 | "\n",
57 | " \n",
58 | "\n",
59 | "\n",
60 | "altitude \n",
61 | "\n",
62 | "altitude \n",
63 | " \n",
64 | "\n",
65 | "\n",
66 | "oxygen_density \n",
67 | "\n",
68 | "oxygen_density \n",
69 | " \n",
70 | "\n",
71 | "\n",
72 | "altitude->oxygen_density \n",
73 | " \n",
74 | " \n",
75 | " \n",
76 | "\n",
77 | "\n",
78 | "temperature \n",
79 | "\n",
80 | "temperature \n",
81 | " \n",
82 | "\n",
83 | "\n",
84 | "altitude->temperature \n",
85 | " \n",
86 | " \n",
87 | " \n",
88 | "\n",
89 | "\n",
90 | "risk_of_death \n",
91 | "\n",
92 | "risk_of_death \n",
93 | " \n",
94 | "\n",
95 | "\n",
96 | "altitude->risk_of_death \n",
97 | " \n",
98 | " \n",
99 | " \n",
100 | "\n",
101 | "\n",
102 | "oxygen_density->risk_of_death \n",
103 | " \n",
104 | " \n",
105 | " \n",
106 | "\n",
107 | "\n",
108 | "temperature->oxygen_density \n",
109 | " \n",
110 | " \n",
111 | " \n",
112 | "\n",
113 | "\n",
114 | "temperature->risk_of_death \n",
115 | " \n",
116 | " \n",
117 | " \n",
118 | "\n",
119 | "\n",
120 | "mehendretex \n",
121 | "\n",
122 | "mehendretex \n",
123 | " \n",
124 | "\n",
125 | "\n",
126 | "mehendretex->risk_of_death \n",
127 | " \n",
128 | " \n",
129 | " \n",
130 | " \n",
131 | " \n"
132 | ],
133 | "text/plain": [
134 | ""
135 | ]
136 | },
137 | "execution_count": 10,
138 | "metadata": {},
139 | "output_type": "execute_result"
140 | }
141 | ],
142 | "source": [
143 | "graph"
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": null,
149 | "id": "81409a0e",
150 | "metadata": {},
151 | "outputs": [],
152 | "source": [
153 | "true_dag = np.array(\n",
154 | " [\n",
155 | " [0, 1, 1, 1, 0],\n",
156 | " [0, 0, 0, 1, 0],\n",
157 | " [0, 1, 0, 1, 0],\n",
158 | " [0, 0, 0, 0, 0],\n",
159 | " [0, 0, 0, 1, 0]\n",
160 | " ]\n",
161 | ")"
162 | ]
163 | }
164 | ],
165 | "metadata": {
166 | "kernelspec": {
167 | "display_name": "Python [conda env:causal_book_py39_exprmnt_cuda117]",
168 | "language": "python",
169 | "name": "conda-env-causal_book_py39_exprmnt_cuda117-py"
170 | },
171 | "language_info": {
172 | "codemirror_mode": {
173 | "name": "ipython",
174 | "version": 3
175 | },
176 | "file_extension": ".py",
177 | "mimetype": "text/x-python",
178 | "name": "python",
179 | "nbconvert_exporter": "python",
180 | "pygments_lexer": "ipython3",
181 | "version": "3.9.16"
182 | }
183 | },
184 | "nbformat": 4,
185 | "nbformat_minor": 5
186 | }
187 |
--------------------------------------------------------------------------------
/Jane the Discoverer/Jane The Discoverer.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 2,
6 | "id": "9880c3b4",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "import os\n",
11 | "from itertools import combinations\n",
12 | "\n",
13 | "import numpy as np\n",
14 | "from scipy import linalg \n",
15 | "from scipy import stats \n",
16 | "\n",
17 | "import matplotlib.pyplot as plt\n",
18 | "\n",
19 | "from langchain.agents import load_tools, initialize_agent\n",
20 | "from langchain.agents import AgentType\n",
21 | "\n",
22 | "from langchain.chat_models import ChatOpenAI\n",
23 | "\n",
24 | "from castle.common import GraphDAG\n",
25 | "from castle.metrics import MetricsDAG\n",
26 | "from castle.algorithms import PC\n",
27 | "\n",
28 | "from castle.common.priori_knowledge import PrioriKnowledge"
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": 3,
34 | "id": "e1e41fe7",
35 | "metadata": {},
36 | "outputs": [],
37 | "source": [
38 | "COLORS = [\n",
39 | " '#00B0F0',\n",
40 | " '#FF0000',\n",
41 | " '#B0F000'\n",
42 | "]"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "id": "5fd7352f",
49 | "metadata": {},
50 | "outputs": [],
51 | "source": [
52 | "def check_if_dag(A):\n",
53 | " return np.trace(linalg.expm(A * A)) - A.shape[0] == 0"
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "id": "f6185948",
59 | "metadata": {},
60 | "source": [
61 | "# Jane The Discoverer\n",
62 | "\n",
63 | "A part of [CausalPython](https://causalpython.io) series on causality.\n",
64 | "\n",
65 | " \n",
66 | " "
67 | ]
68 | },
69 | {
70 | "cell_type": "markdown",
71 | "id": "ff5c5070",
72 | "metadata": {},
73 | "source": [
74 | "## Create the environment\n",
75 | "\n",
76 | "\n",
77 | "To run this notebook use the environment called `causal-nlp-openai-langchain`.\n",
78 | "\n",
79 | "To create it: `conda env create -f causal-nlp-openai-langchain.yml`\n",
80 | "\n",
81 | "You'll need [Conda](https://docs.conda.io/en/latest/) to install it."
82 | ]
83 | },
84 | {
85 | "cell_type": "markdown",
86 | "id": "cc77d263",
87 | "metadata": {},
88 | "source": [
89 | "## Set the key"
90 | ]
91 | },
92 | {
93 | "cell_type": "code",
94 | "execution_count": 4,
95 | "id": "168cfd07",
96 | "metadata": {},
97 | "outputs": [],
98 | "source": [
99 | "with open(r'../../__keys/openai.dat') as f:\n",
100 | " key = f.read()\n",
101 | " \n",
102 | "os.environ['OPENAI_API_KEY'] = key"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "id": "231ee068",
108 | "metadata": {},
109 | "source": [
110 | "## Generate the data"
111 | ]
112 | },
113 | {
114 | "cell_type": "code",
115 | "execution_count": 9,
116 | "id": "11e971bb",
117 | "metadata": {},
118 | "outputs": [],
119 | "source": [
120 | "all_vars = {\n",
121 | " 'altitude': 0,\n",
122 | " 'oxygen_density': 1,\n",
123 | " 'temperature': 2,\n",
124 | " 'risk_of_death': 3,\n",
125 | " 'mehendretex': 4\n",
126 | "}"
127 | ]
128 | },
129 | {
130 | "cell_type": "code",
131 | "execution_count": 10,
132 | "id": "b1c316e5",
133 | "metadata": {},
134 | "outputs": [],
135 | "source": [
136 | "SAMPLE_SIZE = 1000\n",
137 | "\n",
138 | "altitude = stats.halfnorm.rvs(scale=2000, size=SAMPLE_SIZE)\n",
139 | "temperature = 25 - altitude / 100 + stats.norm.rvs(\n",
140 | " loc=0,\n",
141 | " scale=2,\n",
142 | " size=SAMPLE_SIZE\n",
143 | ")\n",
144 | "\n",
145 | "mehendretex = stats.halfnorm.rvs(size=SAMPLE_SIZE)\n",
146 | "\n",
147 | "oxygen_density = np.clip(\n",
148 | " 1 - altitude / 8000 \n",
149 | " - temperature / 50 \n",
150 | " + stats.norm.rvs(size=SAMPLE_SIZE) / 20,\n",
151 | " 0, \n",
152 | " 1)\n",
153 | "\n",
154 | "risk_of_death = np.clip(\n",
155 | " altitude / 20000 \n",
156 | " + np.abs(temperature) / 100 \n",
157 | " - oxygen_density / 5 \n",
158 | " - mehendretex / 5\n",
159 | " + stats.norm.rvs(size=SAMPLE_SIZE) / 10,\n",
160 | " 0,\n",
161 | " 1\n",
162 | ")"
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": 11,
168 | "id": "0ee3773e",
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "dataset = np.stack(\n",
173 | " [\n",
174 | " altitude,\n",
175 | " oxygen_density,\n",
176 | " temperature,\n",
177 | " risk_of_death,\n",
178 | " mehendretex\n",
179 | " ]\n",
180 | ").T"
181 | ]
182 | },
183 | {
184 | "cell_type": "code",
185 | "execution_count": 12,
186 | "id": "2143620c",
187 | "metadata": {},
188 | "outputs": [],
189 | "source": [
190 | "true_dag = np.array(\n",
191 | " [\n",
192 | " [0, 1, 1, 1, 0],\n",
193 | " [0, 0, 0, 1, 0],\n",
194 | " [0, 1, 0, 1, 0],\n",
195 | " [0, 0, 0, 0, 0],\n",
196 | " [0, 0, 0, 1, 0]\n",
197 | " ]\n",
198 | ")"
199 | ]
200 | },
201 | {
202 | "cell_type": "markdown",
203 | "id": "19ba2c60",
204 | "metadata": {},
205 | "source": [
206 | "## PC without LLM assistance"
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": 13,
212 | "id": "c48ba2b7",
213 | "metadata": {},
214 | "outputs": [
215 | {
216 | "name": "stdout",
217 | "output_type": "stream",
218 | "text": [
219 | "Is learned matrix a DAG: False\n"
220 | ]
221 | },
222 | {
223 | "data": {
224 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAApMAAAEhCAYAAAAj/CseAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAxUElEQVR4nO3de3hU9Z3H8c8Qkgm3RLkFAiHEigikwZKoDYoXxPAEjGLrI8/SBUSwoqDEeCPQlcC6xlZloSKpKGoVhFRWVFqKZFe5KLCFCMoC68oKJsVgNlAnISuBhLN/+CTrkNucIXNm5pf363nOHzn+5pzvieTzfH/nNi7LsiwBAAAAfugQ7AIAAAAQvmgmAQAA4DeaSQAAAPiNZhIAAAB+o5kEAACA32gmAQAA4DeaSQAAAPiNZhIAAAB+o5kEAACA32gmDbdjxw7l5eXp22+/DXYpbWbLli1yuVxat25dsEsBECJMzDon3XXXXeratWuwy0CYopk03I4dO7Rw4UICFoDRyDogeGgmERCWZem7774LdhkA0Ei4ZtN3330ny7KCXQbQCM1kCPviiy80adIk9e7dW263W0OGDNELL7zQ8N/PnTunJ598UoMHD1anTp100UUXKSUlRUuXLpUk5eXl6dFHH5UkJSUlyeVyyeVyacuWLT7X8O677yolJUVut1uXXHKJli5dqry8PLlcLq9xLpdLs2fP1u9+9zsNGTJEbrdbv//97yVJCxcu1NVXX63u3bsrJiZGI0aM0MqVKxuF4sCBA3XLLbdo/fr1SklJUXR0tC655BL99re/bbK2s2fPav78+YqPj1dMTIzGjBmjzz//3OdjA2CGlrKuPlfefvtt/eQnP1F0dLQWLlyoo0ePyuVy6bXXXmu0PZfLpby8PK91reWxr2pqavTwww+rT58+6ty5s6677joVFxdr4MCBuuuuuxrGvfbaa3K5XNq8ebPuvvtu9erVS507d1ZNTY0OHz6sadOmadCgQercubP69eunrKws7d+/32tf9bcErVq1Sjk5OerTp486deqk66+/Xnv37m2yvsOHD2vcuHHq2rWrEhIS9PDDD6umpsb2caJ96RjsAtC0gwcPauTIkRowYICee+459enTR++//74efPBBVVRUaMGCBfrNb36jvLw8/epXv9J1112ns2fP6j//8z8bLvPMmDFDJ0+e1PPPP6+3335bffv2lSQNHTrUpxo2bdqkn/3sZ7ruuutUWFio2tpaPfvss/rmm2+aHP/OO+9o+/bteuKJJ9SnTx/17t1bknT06FHde++9GjBggCRp165deuCBB3Ts2DE98cQTXtvYt2+fsrOzlZeXpz59+mj16tWaM2eOzpw5o0ceecRr7Lx583TNNdfo5ZdfVmVlpR5//HFlZWXp0KFDioiI8Pl3DSC8tZZ1n3zyiQ4dOqRf/epXSkpKUpcuXWxt35c89tW0adNUWFioxx57TKNHj9bBgwd1++23q7Kyssnxd999t8aPH6833nhD1dXVioyM1Ndff60ePXro6aefVq9evXTy5En9/ve/19VXX629e/dq8ODBXtuYN2+eRowYoZdfflkej0d5eXm64YYbtHfvXl1yySUN486ePatbb71V06dP18MPP6xt27bpH//xHxUbG9soqwEvFkLS2LFjrf79+1sej8dr/ezZs63o6Gjr5MmT1i233GJdccUVLW7nmWeesSRZR44csV3DlVdeaSUkJFg1NTUN66qqqqwePXpY5//TkWTFxsZaJ0+ebHGbdXV11tmzZ61FixZZPXr0sM6dO9fw3xITEy2Xy2Xt27fP6zM333yzFRMTY1VXV1uWZVkffvihJckaN26c17g//OEPliRr586dto8VQHhrLusSExOtiIgI6/PPP/daf+TIEUuS9eqrrzbaliRrwYIFDT/7kse+OHDggCXJevzxx73Wr1mzxpJkTZ06tWHdq6++akmypkyZ0up2a2trrTNnzliDBg2yHnrooYb19Vk5YsQIr6w9evSoFRkZac2YMaNh3dSpUy1J1h/+8AevbY8bN84aPHiwT8eH9ovL3CHo9OnT+rd/+zfdfvvt6ty5s2praxuWcePG6fTp09q1a5euuuoqffrpp7r//vv1/vvvNzuz9Ud1dbX27NmjCRMmKCoqqmF9165dlZWV1eRnRo8erYsvvrjR+g8++EBjxoxRbGysIiIiFBkZqSeeeEInTpxQeXm519hhw4Zp+PDhXusmTZqkyspKffLJJ17rb731Vq+fU1JSJElfffWV7wcKwHgpKSm67LLL/Pqsr3nsi61bt0qS7rzzTq/1d9xxhzp2bPpC4c9//vNG62pra/XUU09p6NChioqKUseOHRUVFaUvvvhChw4dajR+0qRJXrcmJSYmauTIkfrwww+9xrlcrkb5npKSQqaiVTSTIejEiROqra3V888/r8jISK9l3LhxkqSKigrl5ubq2Wef1a5du5SZmakePXropptu0p49ey64hr/97W+yLEtxcXGN/ltT6yQ1XFr6ob/85S/KyMiQJL300kv6+OOPtXv3bs2fP19S4xvh+/Tp02gb9etOnDjhtb5Hjx5eP7vd7ia3CaB9ayqbfOVrHvu6Lalxhnbs2LFRnrVUe05Ojv7hH/5BEyZM0IYNG/Tv//7v2r17t4YPH95k/jWXq+dnaufOnRUdHe21zu126/Tp0y0fGNo97pkMQRdffLEiIiI0efJkzZo1q8kxSUlJ6tixo3JycpSTk6Nvv/1W//qv/6p58+Zp7NixKi0tVefOnS+oBpfL1eT9kcePH2/yM+c/lCNJa9euVWRkpP74xz96hdQ777zT5Daa2nb9uubCFgBa0lQ21efR+Q+XnN9g+ZrHvqjPsG+++Ub9+vVrWF9bW9tovy3VvmrVKk2ZMkVPPfWU1/qKigpddNFFjcY3l6tkKtoKzWQI6ty5s2688Ubt3btXKSkpXpeZm3PRRRfpjjvu0LFjx5Sdna2jR49q6NChfp+t69Kli9LS0vTOO+/o2Wefbajh1KlT+uMf/+jzdlwulzp27Oj1QMx3332nN954o8nxBw4c0Keffup1qfvNN99Ut27dNGLECFvHAKD9sJt1cXFxio6O1meffea1/t133/X62Z88bs51110nSSosLPTKs3Xr1qm2ttbn7bhcrobjrfenP/1Jx44d06WXXtpo/Jo1a5STk9PQmH711VfasWOHpkyZ4s9hAI3QTIaopUuX6tprr9WoUaN03333aeDAgaqqqtLhw4e1YcMGffDBB8rKylJycrLS0tLUq1cvffXVV1qyZIkSExM1aNAgSdKPf/zjhu1NnTpVkZGRGjx4sLp169ZqDYsWLdL48eM1duxYzZkzR3V1dXrmmWfUtWtXnTx50qfjGD9+vBYvXqxJkybpl7/8pU6cOKFnn322URDWi4+P16233qq8vDz17dtXq1atUlFRkX79619f0JlWAGZrLuua43K59Pd///d65ZVX9KMf/UjDhw/XX/7yF7355puNxvqSx74YNmyY/u7v/k7PPfecIiIiNHr0aB04cEDPPfecYmNj1aGDb3ee3XLLLXrttdd0+eWXKyUlRcXFxXrmmWfUv3//JseXl5fr9ttv1z333COPx6MFCxYoOjpaubm5Pu0PaFWwnwBC844cOWLdfffdVr9+/azIyEirV69e1siRI60nn3zSsizLeu6556yRI0daPXv2tKKioqwBAwZY06dPt44ePeq1ndzcXCs+Pt7q0KGDJcn68MMPfa5h/fr11o9//OOG7T/99NPWgw8+aF188cVe4yRZs2bNanIbr7zyijV48GDL7XZbl1xyiZWfn2+tXLmy0ZOXiYmJ1vjx461169ZZw4YNs6KioqyBAwdaixcv9tpe/ROKb731VqPfl5p5OhOA+ZrKuvpcaYrH47FmzJhhxcXFWV26dLGysrKso0ePNnqa27Jaz2NfnT592srJybF69+5tRUdHWz/96U+tnTt3WrGxsV5PYtc/zb179+5G2/jb3/5mTZ8+3erdu7fVuXNn69prr7W2b99uXX/99db111/fMK4+K9944w3rwQcftHr16mW53W5r1KhR1p49e7y2OXXqVKtLly6N9rVgwYJGb+8AzueyLF6nD9+dPXtWV1xxhfr166fNmze36bYHDhyo5ORkW5fRASDc7dixQ9dcc41Wr16tSZMmtdl2t2zZohtvvFFvvfWW7rjjjjbbLnA+nuZGi6ZPn661a9dq69atKiwsVEZGhg4dOqTHHnss2KXBMNu2bVNWVpbi4+PlcrmafUjrh7Zu3arU1NSGb0v63e9+F/hCgQtQVFSkRYsW6U9/+pM++OAD/fM//7Nuv/12DRo0SD/72c+CXR7CXLBylHsm26Fz587p3LlzLY6pf+dZVVWVHnnkEf3P//yPIiMjNWLECG3cuFFjxoxxolS0I9XV1Ro+fLimTZvW5Lv1znfkyBGNGzdO99xzj1atWqWPP/5Y999/v3r16uXT54G2VFdX1+L3ZrtcLkVERCgmJkabN2/WkiVLVFVVpZ49eyozM1P5+fmNXssD2BWsHOUydzuUl5enhQsXtjjmyJEjGjhwoDMFAedxuVxav369JkyY0OyYxx9/XO+9957XS5pnzpypTz/9VDt37nSgSuD/DRw4sMWXe19//fXasmWLcwWh3XMyRzkz2Q798pe/1C233NLimPj4eIeqQTg5ffq0zpw54/N4y7IavSfP7XY3+zS/HTt37mx4IX69sWPHauXKlTp79qwiIyMveB+ArzZs2NDonZU/5MsbNNA+mJijNJPtUHx8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",
225 | "text/plain": [
226 | ""
227 | ]
228 | },
229 | "metadata": {},
230 | "output_type": "display_data"
231 | },
232 | {
233 | "name": "stdout",
234 | "output_type": "stream",
235 | "text": [
236 | "{'fdr': 0.0, 'tpr': 0.5714, 'fpr': 0.0, 'shd': 3, 'nnz': 4, 'precision': 0.5, 'recall': 0.5714, 'F1': 0.5333, 'gscore': 0.0}\n"
237 | ]
238 | }
239 | ],
240 | "source": [
241 | "# PC discovery without LLM assist\n",
242 | "pc = PC(variant='stable')\n",
243 | "pc.learn(dataset)\n",
244 | "\n",
245 | "print(f'Is learned matrix a DAG: {check_if_dag(pc.causal_matrix)}')\n",
246 | "\n",
247 | "# Vizualize\n",
248 | "GraphDAG(\n",
249 | " est_dag=pc.causal_matrix, \n",
250 | " true_dag=true_dag)\n",
251 | "\n",
252 | "plt.show()\n",
253 | "\n",
254 | "# Compute metrics\n",
255 | "metrics = MetricsDAG(\n",
256 | " B_est=pc.causal_matrix, \n",
257 | " B_true=true_dag)\n",
258 | "\n",
259 | "print(metrics.metrics)"
260 | ]
261 | },
262 | {
263 | "cell_type": "code",
264 | "execution_count": 14,
265 | "id": "a354d704",
266 | "metadata": {},
267 | "outputs": [],
268 | "source": [
269 | "# Instantiate and encode priori knowledge\n",
270 | "priori_knowledge = PrioriKnowledge(n_nodes=len(all_vars))"
271 | ]
272 | },
273 | {
274 | "cell_type": "markdown",
275 | "id": "0d5525dc",
276 | "metadata": {},
277 | "source": [
278 | "## Instantiate the GPT agent"
279 | ]
280 | },
281 | {
282 | "cell_type": "code",
283 | "execution_count": 5,
284 | "id": "18ecb64b",
285 | "metadata": {},
286 | "outputs": [],
287 | "source": [
288 | "llm = ChatOpenAI(\n",
289 | " temperature=0, # Temp == 0 => we want clear reasoning\n",
290 | " model='gpt-4')#'gpt-3.5-turbo') "
291 | ]
292 | },
293 | {
294 | "cell_type": "code",
295 | "execution_count": 6,
296 | "id": "0abb0fa4",
297 | "metadata": {},
298 | "outputs": [],
299 | "source": [
300 | "# Load tools\n",
301 | "tools = load_tools(\n",
302 | " [\n",
303 | " \"wikipedia\"\n",
304 | " ], \n",
305 | " llm=llm)"
306 | ]
307 | },
308 | {
309 | "cell_type": "code",
310 | "execution_count": 7,
311 | "id": "e7656e74",
312 | "metadata": {},
313 | "outputs": [],
314 | "source": [
315 | "# Instantiate the agent\n",
316 | "agent = initialize_agent(\n",
317 | " tools, \n",
318 | " llm, \n",
319 | " agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,\n",
320 | " handle_parsing_errors=True,\n",
321 | " verbose=False)"
322 | ]
323 | },
324 | {
325 | "cell_type": "code",
326 | "execution_count": 8,
327 | "id": "e3470579",
328 | "metadata": {},
329 | "outputs": [],
330 | "source": [
331 | "def get_llm_info(llm, agent, var_1, var_2):\n",
332 | " \n",
333 | " out = agent(f\"Does {var_1} cause {var_2} or the other way around?\\\n",
334 | " We assume the following definition of causation:\\\n",
335 | " if we change A, B will also change.\\\n",
336 | " The relationship does not have to be linear or monotonic.\\\n",
337 | " We are interested in all types of causal relationships, including\\\n",
338 | " partial and indirect relationships, given that our definition holds.\\\n",
339 | " \")\n",
340 | " \n",
341 | " print(out)\n",
342 | " \n",
343 | " pred = llm.predict(f'We assume the following definition of causation:\\\n",
344 | " if we change A, B will also change.\\\n",
345 | " Based on the following information: {out[\"output\"]},\\\n",
346 | " print (0,1) if {var_1} causes {var_2},\\\n",
347 | " print (1, 0) if {var_2} causes {var_1}, print (0,0)\\\n",
348 | " if there is no causal relationship between {var_1} and {var_2}.\\\n",
349 | " Finally, print (-1, -1) if you don\\'t know. Importantly, don\\'t try to\\\n",
350 | " make up an answer if you don\\'t know.')\n",
351 | " \n",
352 | " print(pred)\n",
353 | " \n",
354 | " return pred"
355 | ]
356 | },
357 | {
358 | "cell_type": "markdown",
359 | "id": "c69646f5",
360 | "metadata": {},
361 | "source": [
362 | "## Add priori knowledge from the LLM"
363 | ]
364 | },
365 | {
366 | "cell_type": "code",
367 | "execution_count": 15,
368 | "id": "1a65e96a",
369 | "metadata": {
370 | "scrolled": false
371 | },
372 | "outputs": [
373 | {
374 | "name": "stdout",
375 | "output_type": "stream",
376 | "text": [
377 | "altitude oxygen_density\n",
378 | "{'input': 'Does altitude cause oxygen_density or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'Altitude causes changes in oxygen density.'}\n",
379 | "(0,1)\n",
380 | "altitude temperature\n",
381 | "{'input': 'Does altitude cause temperature or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': \"Altitude and temperature interact in complex ways within the Earth's atmosphere, and it's not accurate to say that one causes the other. Changes in altitude can influence temperature, and changes in temperature can also have effects that influence altitude.\"}\n",
382 | "(1, 1)\n",
383 | "altitude risk_of_death\n",
384 | "{'input': 'Does altitude cause risk_of_death or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'Altitude can cause an increased risk of death.'}\n",
385 | "(0,1)\n",
386 | "altitude mehendretex\n",
387 | "{'input': 'Does altitude cause mehendretex or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'I\\'m sorry, but I couldn\\'t find any information on \"mehendretex\". Therefore, it\\'s impossible to determine whether it\\'s caused by altitude or vice versa.'}\n",
388 | "(-1, -1)\n",
389 | "oxygen_density temperature\n",
390 | "{'input': 'Does oxygen_density cause temperature or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': \"The ideal gas law suggests that the density of a gas is related to its temperature, but it doesn't specify a causal relationship. To determine causality, an experiment would need to be conducted where one variable (either oxygen density or temperature) is changed and the effect on the other is observed.\"}\n",
391 | "(-1, -1)\n",
392 | "oxygen_density risk_of_death\n"
393 | ]
394 | },
395 | {
396 | "name": "stderr",
397 | "output_type": "stream",
398 | "text": [
399 | "C:\\Users\\aleks\\anaconda3\\envs\\causal-nlp-openai-langchain\\lib\\site-packages\\wikipedia\\wikipedia.py:389: GuessedAtParserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for this system (\"lxml\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n",
400 | "\n",
401 | "The code that caused this warning is on line 389 of the file C:\\Users\\aleks\\anaconda3\\envs\\causal-nlp-openai-langchain\\lib\\site-packages\\wikipedia\\wikipedia.py. To get rid of this warning, pass the additional argument 'features=\"lxml\"' to the BeautifulSoup constructor.\n",
402 | "\n",
403 | " lis = BeautifulSoup(html).find_all('li')\n"
404 | ]
405 | },
406 | {
407 | "name": "stdout",
408 | "output_type": "stream",
409 | "text": [
410 | "{'input': 'Does oxygen_density cause risk_of_death or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'Changes in oxygen density can cause changes in the risk of death. Both low oxygen density (hypoxia) and high oxygen density (hyperoxia) can lead to health problems and increase the risk of death.'}\n",
411 | "(0,1)\n",
412 | "oxygen_density mehendretex\n",
413 | "{'input': 'Does oxygen_density cause mehendretex or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'I\\'m sorry, but without more information on what \"mehendretex\" is, I can\\'t determine if there\\'s a causal relationship between it and oxygen density.'}\n",
414 | "(-1, -1)\n",
415 | "temperature risk_of_death\n",
416 | "{'input': 'Does temperature cause risk_of_death or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': \"Yes, changes in temperature can influence the risk of death, particularly in vulnerable populations. However, it's not accurate to say that temperature alone causes changes in the risk of death, as this is a complex issue with many contributing factors.\"}\n",
417 | "(0,1)\n",
418 | "temperature mehendretex\n",
419 | "{'input': 'Does temperature cause mehendretex or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'I\\'m sorry, but I couldn\\'t find any information on \"mehendretex\". Therefore, it\\'s impossible to determine whether it has any causal relationship with temperature.'}\n",
420 | "(-1, -1)\n",
421 | "risk_of_death mehendretex\n",
422 | "{'input': 'Does risk_of_death cause mehendretex or the other way around? We assume the following definition of causation: if we change A, B will also change. The relationship does not have to be linear or monotonic. We are interested in all types of causal relationships, including partial and indirect relationships, given that our definition holds. ', 'output': 'I\\'m sorry, but I couldn\\'t find any information on \"mehendretex\". Therefore, it\\'s impossible to determine a causal relationship between \"risk_of_death\" and \"mehendretex\". Could you please provide more context or check if \"mehendretex\" is spelled correctly?'}\n",
423 | "(-1, -1)\n",
424 | "\n",
425 | "LLM knowledge vs true DAG\n",
426 | "\n",
427 | "Checking if priori graph is a DAG: True\n"
428 | ]
429 | },
430 | {
431 | "data": {
432 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAApMAAAEhCAYAAAAj/CseAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAxHklEQVR4nO3de3RV5Z3/8c9JSE64JcotEAghVkQgDZZEbVC8IIYVMIqtS9bQAUSwIqDEeI30J4FxGluVgYqkoihVEVIZUWkpkhnlosAUIigDjCMjmBSDmUBNQkYCCfv3R1dSDyeXsw85+5zz5P1aa/+Rx733+e5IPuv77NtxWZZlCQAAAPBDRLALAAAAQPiimQQAAIDfaCYBAADgN5pJAAAA+I1mEgAAAH6jmQQAAIDfaCYBAADgN5pJAAAA+I1mEgAAAH6jmTTcjh07lJ+fr2+//TbYpbSbLVu2yOVyad26dcEuBUCIMDHrnHTXXXepW7duwS4DYYpm0nA7duzQwoULCVgARiPrgOChmURAWJal7777LthlAICXcM2m7777TpZlBbsMwAvNZAj74osvNHnyZPXp00dut1tDhw7VCy+80PTfz507p6eeekpDhgxR586dddFFFyk1NVVLly6VJOXn5+uRRx6RJCUnJ8vlcsnlcmnLli0+1/Duu+8qNTVVbrdbl1xyiZYuXar8/Hy5XC6P9Vwul+bOnavf/va3Gjp0qNxut373u99JkhYuXKirr75aPXr0UGxsrEaOHKmVK1d6heKgQYN0yy23aP369UpNTVVMTIwuueQS/eY3v2m2trNnz2r+/PlKSEhQbGysxo4dq88//9znYwNghtayrjFX3n77bf3oRz9STEyMFi5cqKNHj8rlcmnVqlVe+3O5XMrPz/cYayuPfVVXV6eHHnpIffv2VZcuXXTdddeppKREgwYN0l133dW03qpVq+RyubR582bdfffd6t27t7p06aK6ujodPnxY06dP1+DBg9WlSxf1799f2dnZ2r9/v8dnNd4S9MYbbyg3N1d9+/ZV586ddf3112vv3r3N1nf48GGNHz9e3bp1U2Jioh566CHV1dXZPk50LJ2CXQCad/DgQY0aNUoDBw7Uc889p759++r999/XAw88oMrKSi1YsEC//vWvlZ+fr1/84he67rrrdPbsWf3Xf/1X02WemTNn6uTJk3r++ef19ttvq1+/fpKkYcOG+VTDpk2b9JOf/ETXXXedioqKVF9fr2effVbffPNNs+u/88472r59u5588kn17dtXffr0kSQdPXpU9957rwYOHChJ2rVrl+6//34dO3ZMTz75pMc+9u3bp5ycHOXn56tv375avXq15s2bpzNnzujhhx/2WPeJJ57QNddco5dfflnV1dV67LHHlJ2drUOHDikyMtLn3zWA8NZW1n3yySc6dOiQfvGLXyg5OVldu3a1tX9f8thX06dPV1FRkR599FGNGTNGBw8e1O23367q6upm17/77rs1YcIEvf7666qtrVVUVJS+/vpr9ezZU08//bR69+6tkydP6ne/+52uvvpq7d27V0OGDPHYxxNPPKGRI0fq5ZdfVlVVlfLz83XDDTdo7969uuSSS5rWO3v2rG699VbNmDFDDz30kLZt26Z/+qd/UlxcnFdWAx4shKRx48ZZAwYMsKqqqjzG586da8XExFgnT560brnlFuuKK65odT/PPPOMJck6cuSI7RquvPJKKzEx0aqrq2saq6mpsXr27Gmd/09HkhUXF2edPHmy1X02NDRYZ8+etRYtWmT17NnTOnfuXNN/S0pKslwul7Vv3z6PbW6++WYrNjbWqq2ttSzLsj788ENLkjV+/HiP9X7/+99bkqydO3faPlYA4a2lrEtKSrIiIyOtzz//3GP8yJEjliTr1Vdf9dqXJGvBggVNP/uSx744cOCAJcl67LHHPMbXrFljSbKmTZvWNPbqq69akqypU6e2ud/6+nrrzJkz1uDBg60HH3ywabwxK0eOHOmRtUePHrWioqKsmTNnNo1NmzbNkmT9/ve/99j3+PHjrSFDhvh0fOi4uMwdgk6fPq1///d/1+23364uXbqovr6+aRk/frxOnz6tXbt26aqrrtKnn36q2bNn6/33329xZuuP2tpa7dmzRxMnTlR0dHTTeLdu3ZSdnd3sNmPGjNHFF1/sNf7BBx9o7NixiouLU2RkpKKiovTkk0/qxIkTqqio8Fh3+PDhGjFihMfY5MmTVV1drU8++cRj/NZbb/X4OTU1VZL01Vdf+X6gAIyXmpqqyy67zK9tfc1jX2zdulWSdOedd3qM33HHHerUqfkLhT/96U+9xurr6/XLX/5Sw4YNU3R0tDp16qTo6Gh98cUXOnTokNf6kydP9rg1KSkpSaNGjdKHH37osZ7L5fLK99TUVDIVbaKZDEEnTpxQfX29nn/+eUVFRXks48ePlyRVVlYqLy9Pzz77rHbt2qWsrCz17NlTN910k/bs2XPBNfz1r3+VZVmKj4/3+m/NjUlqurT0fX/+85+VmZkpSXrppZf08ccfa/fu3Zo/f74k7xvh+/bt67WPxrETJ054jPfs2dPjZ7fb3ew+AXRszWWTr3zNY1/3JXlnaKdOnbzyrLXac3Nz9f/+3//TxIkTtWHDBv3Hf/yHdu/erREjRjSbfy3l6vmZ2qVLF8XExHiMud1unT59uvUDQ4fHPZMh6OKLL1ZkZKSmTJmiOXPmNLtOcnKyOnXqpNzcXOXm5urbb7/Vv/3bv+mJJ57QuHHjVFZWpi5dulxQDS6Xq9n7I48fP97sNuc/lCNJa9euVVRUlP7whz94hNQ777zT7D6a23fjWEthCwCtaS6bGvPo/IdLzm+wfM1jXzRm2DfffKP+/fs3jdfX13t9bmu1v/HGG5o6dap++ctfeoxXVlbqoosu8lq/pVwlU9FeaCZDUJcuXXTjjTdq7969Sk1N9bjM3JKLLrpId9xxh44dO6acnBwdPXpUw4YN8/tsXdeuXZWenq533nlHzz77bFMNp06d0h/+8Aef9+NyudSpUyePB2K+++47vf76682uf+DAAX366acel7rffPNNde/eXSNHjrR1DAA6DrtZFx8fr5iYGH322Wce4++++67Hz/7kcUuuu+46SVJRUZFHnq1bt0719fU+78flcjUdb6M//vGPOnbsmC699FKv9desWaPc3NymxvSrr77Sjh07NHXqVH8OA/BCMxmili5dqmuvvVajR4/Wfffdp0GDBqmmpkaHDx/Whg0b9MEHHyg7O1spKSlKT09X79699dVXX2nJkiVKSkrS4MGDJUk//OEPm/Y3bdo0RUVFaciQIerevXubNSxatEgTJkzQuHHjNG/ePDU0NOiZZ55Rt27ddPLkSZ+OY8KECVq8eLEmT56sn//85zpx4oSeffZZryBslJCQoFtvvVX5+fnq16+f3njjDRUXF+tXv/rVBZ1pBWC2lrKuJS6XS//4j/+oV155RT/4wQ80YsQI/fnPf9abb77pta4veeyL4cOH6x/+4R/03HPPKTIyUmPGjNGBAwf03HPPKS4uThERvt15dsstt2jVqlW6/PLLlZqaqpKSEj3zzDMaMGBAs+tXVFTo9ttv1z333KOqqiotWLBAMTExysvL8+nzgDYF+wkgtOzIkSPW3XffbfXv39+KioqyevfubY0aNcp66qmnLMuyrOeee84aNWqU1atXLys6OtoaOHCgNWPGDOvo0aMe+8nLy7MSEhKsiIgIS5L14Ycf+lzD+vXrrR/+8IdN+3/66aetBx54wLr44os91pNkzZkzp9l9vPLKK9aQIUMst9ttXXLJJVZBQYG1cuVKrycvk5KSrAkTJljr1q2zhg8fbkVHR1uDBg2yFi9e7LG/xicU33rrLa/fl1p4OhOA+ZrLusZcaU5VVZU1c+ZMKz4+3uratauVnZ1tHT161OtpbstqO499dfr0aSs3N9fq06ePFRMTY/34xz+2du7cacXFxXk8id34NPfu3bu99vHXv/7VmjFjhtWnTx+rS5cu1rXXXmtt377duv76663rr7++ab3GrHz99detBx54wOrdu7fldrut0aNHW3v27PHY57Rp06yuXbt6fdaCBQu83t4BnM9lWbxOH747e/asrrjiCvXv31+bN29u130PGjRIKSkpti6jA0C427Fjh6655hqtXr1akydPbrf9btmyRTfeeKPeeust3XHHHe22X+B8PM2NVs2YMUNr167V1q1bVVRUpMzMTB06dEiPPvposEuDYbZt26bs7GwlJCTI5XK1+JDW923dulVpaWlN35b029/+NvCFAheguLhYixYt0h//+Ed98MEH+pd/+RfdfvvtGjx4sH7yk58EuzyEuWDlKPdMdkDnzp3TuXPnWl2n8Z1nNTU1evjhh/W///u/ioqK0siRI7Vx40aNHTvWiVLRgdTW1mrEiBGaPn16s+/WO9+RI0c0fvx43XPPPXrjjTf08ccfa/bs2erdu7dP2wPtqaGhodXvzXa5XIqMjFRsbKw2b96sJUuWqKamRr169VJWVpYKCgq8XssD2BWsHOUydweUn5+vhQsXtrrOkSNHNGjQIGcKAs7jcrm0fv16TZw4scV1HnvsMb333nseL2meNWuWPv30U+3cudOBKoG/GzRoUKsv977++uu1ZcsW5wpCh+dkjnJmsgP6+c9/rltuuaXVdRISEhyqBuHk9OnTOnPmjM/rW5bl9Z48t9vd4tP8duzcubPphfiNxo0bp5UrV+rs2bOKioq64M8AfLVhwwavd1Z+ny9v0EDHYGKO0kx2QAkJ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325VVZUlyfr222+tc+fOtbl8++23liSrqqrKp/2vXbvWioqKsl566SXr4MGD1rx586yuXbtaX331VbPrf/nll1aXLl2sefPmWQcPHrReeuklKyoqylq3bp2t4wpH5CgLS2gs5OjfuCzLz7baT+fOndPXX3+t7t27t+vp2+rqaiUmJqqsrEyxsbHttt9AomZnhGPNUuDqtixLNTU1SkhIUESE77dNV1dXKy4uTlVVVT7VY3f9q6++WiNHjlRhYWHT2NChQzVx4kQVFBR4rf/YY4/pvffe06FDh5rGZs2apU8//VQ7d+708ajCEzn6d+FYsxSedVPz35Gjnhy/zB0REaEBAwYEbP+xsbFh84+8ETU7IxxrlgJTd1xcnN/b+nopqnG989d3u91yu90eY2fOnFFJSYkef/xxj/HMzEzt2LGj2f3v3LlTmZmZHmPjxo3TypUrdfbsWUVFRflUZzgiR72FY81SeNZNzX9Djv4dD+AA8El0dLT69u2rxMREn7fp1q2b1/oLFixQfn6+x1hlZaUaGhoUHx/vMR4fH6/jx483u+/jx483u359fb0qKyvVr18/n+sEACeYmqM0kwB8EhMToyNHjujMmTM+b2NZltdl2PNn0993/rrNbd/W+s2NA0AoMDVHjWkm3W63FixY0OovONRQszPCsWYpNOuOiYlRTExMu++3V69eioyM9Jo9V1RUeM2aG/Xt27fZ9Tt16qSePXu2e40dQSj+m2tLONYshWfd1Nw+TMxRxx/AAYDmXH311UpLS9Py5cubxoYNG6bbbrutxRvHN2zYoIMHDzaN3Xfffdq3b5/xD+AAQHOClqO2nv0GgABpfKXFypUrrYMHD1o5OTlW165draNHj1qWZVmPP/64NWXKlKb1G19p8eCDD1oHDx60Vq5c2WFeDQQAzQlWjhpzmRtAeJs0aZJOnDihRYsWqby8XCkpKdq4caOSkpIkSeXl5SotLW1aPzk5WRs3btSDDz6oF154QQkJCfrNb36jn/70p8E6BAAIqmDlKJe5AQAA4Dff37QJAAAAnIdmEgAAAH4zpplcvny5kpOTFRMTo7S0NG3fvj3YJbVo27Ztys7OVkJCglwul955551gl9SmgoICXXnllerevbv69OmjiRMn6vPPPw92Wa0qLCxUampq0zcfZGRk6E9/+lOwy7KloKBALpdLOTk5wS4FHUA45agUfllKjgYHORp4RjSTRUVFysnJ0fz587V3716NHj1aWVlZHjeZhpLa2lqNGDFCy5YtC3YpPtu6davmzJmjXbt2qbi4WPX19crMzFRtbW2wS2vRgAED9PTTT2vPnj3as2ePxowZo9tuu00HDhwIdmk+2b17t1asWKHU1NRgl4IOINxyVAq/LCVHnUeOOqQdn0gPmquuusqaNWuWx9jll19uPf7440GqyHeSrPXr1we7DNsqKiosSdbWrVuDXYotF198sfXyyy8Hu4w21dTUWIMHD7aKi4ut66+/3po3b16wS4LhwjlHLSs8s5QcDSxy1Dlhf2ay8YvNz/+i8ta+2BwXrqqqSpLUo0ePIFfim4aGBq1du1a1tbXKyMgIdjltmjNnjiZMmKCxY8cGuxR0AORocJCjgUWOOifs3zPpzxeb48JYlqXc3Fxde+21SklJCXY5rdq/f78yMjJ0+vRpdevWTevXr9ewYcOCXVar1q5dq08++US7d+8OdinoIMhR55GjgUWOOivsm8lGdr/YHP6bO3euPvvsM3300UfBLqVNQ4YM0b59+/Ttt9/qX//1XzVt2jRt3bo1ZIOwrKxM8+bN0+bNmwPy3a1Aa8hR55CjgUOOOi/sm0l/vtgc/rv//vv13nvvadu2bRowYECwy2lTdHS0Lr30UklSenq6du/eraVLl+rFF18McmXNKykpUUVFhdLS0prGGhoatG3bNi1btkx1dXWKjIwMYoUwETnqLHI0sMhR54X9PZPR0dFKS0tTcXGxx3hxcbFGjRoVpKrMY1mW5s6dq7ffflsffPCBkpOTg12SXyzLUl1dXbDLaNFNN92k/fv3a9++fU1Lenq6fvazn2nfvn0EIAKCHHUGOeoMctR5YX9mUpJyc3M1ZcoUpaenKyMjQytWrFBpaalmzZoV7NKaderUKR0+fLjp5yNHjmjfvn3q0aOHBg4cGMTKWjZnzhy9+eabevfdd9W9e/emMxhxcXHq3LlzkKtr3hNPPKGsrCwlJiaqpqZGa9eu1ZYtW7Rp06Zgl9ai7t27e90/1bVrV/Xs2TPk76tCeAu3HJXCL0vJUWeQo0EQvAfJ29cLL7xgJSUlWdHR0dbIkSND+lULH374oSXJa5k2bVqwS2tRc/VKsl599dVgl9aiu+++u+nfRO/eva2bbrrJ2rx5c7DLso1XWsAp4ZSjlhV+WUqOBg85Glguy7IsJ5tXAAAAmCPs75kEAABA8NBMAgAAwG80kwAAAPAbzSQAAAD8RjMJAAAAv9FMAgAAwG80kwAAAPAbzSQAAAD8RjMJAAAAv9FMAgAAwG80kwAAAPDb/wc3sjK/33qwaAAAAABJRU5ErkJggg==",
433 | "text/plain": [
434 | ""
435 | ]
436 | },
437 | "metadata": {},
438 | "output_type": "display_data"
439 | },
440 | {
441 | "name": "stdout",
442 | "output_type": "stream",
443 | "text": [
444 | "\n",
445 | "Running PC\n"
446 | ]
447 | },
448 | {
449 | "data": {
450 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAApMAAAEhCAYAAAAj/CseAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAxR0lEQVR4nO3de3hU9Z3H8c8Qkgm3RLkFAiHEigikwZKoDYoXxPAEjGLrI8/SBUSwoqDEeCPQlUBdY6uyUJFUFLUKQiorKi1FsqtcFNhCBGWBdWUFk2IwG6iTkJVAwtk/+iR1yG3OkDkz88v79Tznj5z+5pzvoc7n+f7ObVyWZVkCAAAA/NAh2AUAAAAgfNFMAgAAwG80kwAAAPAbzSQAAAD8RjMJAAAAv9FMAgAAwG80kwAAAPAbzSQAAAD8RjMJAAAAv9FMGm7Hjh3Ky8vTt99+G+xS2syWLVvkcrm0bt26YJcCIESYmHVOuuuuu9S1a9dgl4EwRTNpuB07dmjhwoUELACjkXVA8NBMIiAsy9J3330X7DIAoJFwzabvvvtOlmUFuwygEZrJEPbFF19o0qRJ6t27t9xut4YMGaIXXnih4X8/d+6cnnzySQ0ePFidOnXSRRddpJSUFC1dulSSlJeXp0cffVSSlJSUJJfLJZfLpS1btvhcw7vvvquUlBS53W5dcsklWrp0qfLy8uRyubzGuVwuzZ49W7/97W81ZMgQud1u/e53v5MkLVy4UFdffbW6d++umJgYjRgxQitXrmwUigMHDtQtt9yi9evXKyUlRdHR0brkkkv0m9/8psnazp49q/nz5ys+Pl4xMTEaM2aMPv/8c5+PDYAZWsq6+lx5++239aMf/UjR0dFauHChjh49KpfLpddee63R9lwul/Ly8rzWtZbHvqqpqdHDDz+sPn36qHPnzrruuutUXFysgQMH6q677moY99prr8nlcmnz5s26++671atXL3Xu3Fk1NTU6fPiwpk2bpkGDBqlz587q16+fsrKytH//fq991d8StGrVKuXk5KhPnz7q1KmTrr/+eu3du7fJ+g4fPqxx48apa9euSkhI0MMPP6yamhrbx4n2pWOwC0DTDh48qJEjR2rAgAF67rnn1KdPH73//vt68MEHVVFRoQULFujXv/618vLy9Itf/ELXXXedzp49q//6r/9quMwzY8YMnTx5Us8//7zefvtt9e3bV5I0dOhQn2rYtGmTfvKTn+i6665TYWGhamtr9eyzz+qbb75pcvw777yj7du364knnlCfPn3Uu3dvSdLRo0d17733asCAAZKkXbt26YEHHtCxY8f0xBNPeG1j3759ys7OVl5envr06aPVq1drzpw5OnPmjB555BGvsfPmzdM111yjl19+WZWVlXr88ceVlZWlQ4cOKSIiwud/awDhrbWs++STT3To0CH94he/UFJSkrp06WJr+77ksa+mTZumwsJCPfbYYxo9erQOHjyo22+/XZWVlU2Ov/vuuzV+/Hi98cYbqq6uVmRkpL7++mv16NFDTz/9tHr16qWTJ0/qd7/7na6++mrt3btXgwcP9trGvHnzNGLECL388svyeDzKy8vTDTfcoL179+qSSy5pGHf27Fndeuutmj59uh5++GFt27ZNv/zlLxUbG9soqwEvFkLS2LFjrf79+1sej8dr/ezZs63o6Gjr5MmT1i233GJdccUVLW7nmWeesSRZR44csV3DlVdeaSUkJFg1NTUN66qqqqwePXpY5/+nI8mKjY21Tp482eI26+rqrLNnz1qLFi2yevToYZ07d67hf0tMTLRcLpe1b98+r8/cfPPNVkxMjFVdXW1ZlmV9+OGHliRr3LhxXuN+//vfW5KsnTt32j5WAOGtuaxLTEy0IiIirM8//9xr/ZEjRyxJ1quvvtpoW5KsBQsWNPztSx774sCBA5Yk6/HHH/dav2bNGkuSNXXq1IZ1r776qiXJmjJlSqvbra2ttc6cOWMNGjTIeuihhxrW12fliBEjvLL26NGjVmRkpDVjxoyGdVOnTrUkWb///e+9tj1u3Dhr8ODBPh0f2i8uc4eg06dP69///d91++23q3PnzqqtrW1Yxo0bp9OnT2vXrl266qqr9Omnn+r+++/X+++/3+zM1h/V1dXas2ePJkyYoKioqIb1Xbt2VVZWVpOfGT16tC6++OJG6z/44AONGTNGsbGxioiIUGRkpJ544gmdOHFC5eXlXmOHDRum4cOHe62bNGmSKisr9cknn3itv/XWW73+TklJkSR99dVXvh8oAOOlpKTosssu8+uzvuaxL7Zu3SpJuvPOO73W33HHHerYsekLhT/96U8brautrdVTTz2loUOHKioqSh07dlRUVJS++OILHTp0qNH4SZMmed2alJiYqJEjR+rDDz/0GudyuRrle0pKCpmKVtFMhqATJ06otrZWzz//vCIjI72WcePGSZIqKiqUm5urZ599Vrt27VJmZqZ69Oihm266SXv27LngGv7617/KsizFxcU1+t+aWiep4dLS9/35z39WRkaGJOmll17Sxx9/rN27d2v+/PmSGt8I36dPn0bbqF934sQJr/U9evTw+tvtdje5TQDtW1PZ5Ctf89jXbUmNM7Rjx46N8qyl2nNycvRP//RPmjBhgjZs2KD/+I//0O7duzV8+PAm86+5XD0/Uzt37qzo6GivdW63W6dPn275wNDucc9kCLr44osVERGhyZMna9asWU2OSUpKUseOHZWTk6OcnBx9++23+rd/+zfNmzdPY8eOVWlpqTp37nxBNbhcribvjzx+/HiTnzn/oRxJWrt2rSIjI/WHP/zBK6TeeeedJrfR1Lbr1zUXtgDQkqayqT6Pzn+45PwGy9c89kV9hn3zzTfq169fw/ra2tpG+22p9lWrVmnKlCl66qmnvNZXVFTooosuajS+uVwlU9FWaCZDUOfOnXXjjTdq7969SklJ8brM3JyLLrpId9xxh44dO6bs7GwdPXpUQ4cO9ftsXZcuXZSWlqZ33nlHzz77bEMNp06d0h/+8Aeft+NyudSxY0evB2K+++47vfHGG02OP3DggD799FOvS91vvvmmunXrphEjRtg6BgDth92si4uLU3R0tD777DOv9e+++67X3/7kcXOuu+46SVJhYaFXnq1bt061tbU+b8flcjUcb70//vGPOnbsmC699NJG49esWaOcnJyGxvSrr77Sjh07NGXKFH8OA2iEZjJELV26VNdee61GjRql++67TwMHDlRVVZUOHz6sDRs26IMPPlBWVpaSk5OVlpamXr166auvvtKSJUuUmJioQYMGSZJ++MMfNmxv6tSpioyM1ODBg9WtW7dWa1i0aJHGjx+vsWPHas6cOaqrq9Mzzzyjrl276uTJkz4dx/jx47V48WJNmjRJP//5z3XixAk9++yzjYKwXnx8vG699Vbl5eWpb9++WrVqlYqKivSrX/3qgs60AjBbc1nXHJfLpX/8x3/UK6+8oh/84AcaPny4/vznP+vNN99sNNaXPPbFsGHD9A//8A967rnnFBERodGjR+vAgQN67rnnFBsbqw4dfLvz7JZbbtFrr72myy+/XCkpKSouLtYzzzyj/v37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",
451 | "text/plain": [
452 | ""
453 | ]
454 | },
455 | "metadata": {},
456 | "output_type": "display_data"
457 | },
458 | {
459 | "name": "stdout",
460 | "output_type": "stream",
461 | "text": [
462 | "{'fdr': 0.0, 'tpr': 1.0, 'fpr': 0.0, 'shd': 0, 'nnz': 7, 'precision': 0.875, 'recall': 1.0, 'F1': 0.9333, 'gscore': 0.8571}\n"
463 | ]
464 | }
465 | ],
466 | "source": [
467 | "for var_1, var_2 in combinations(all_vars.keys(), r=2):\n",
468 | " print(var_1, var_2)\n",
469 | " out = get_llm_info(llm, agent, var_1, var_2)\n",
470 | " if out=='(0,1)':\n",
471 | " priori_knowledge.add_required_edges(\n",
472 | " [(all_vars[var_1], all_vars[var_2])]\n",
473 | " )\n",
474 | " \n",
475 | " priori_knowledge.add_forbidden_edges(\n",
476 | " [(all_vars[var_2], all_vars[var_1])]\n",
477 | " )\n",
478 | "\n",
479 | " elif out=='(1,0)':\n",
480 | " priori_knowledge.add_required_edges(\n",
481 | " [(all_vars[var_2], all_vars[var_1])]\n",
482 | " )\n",
483 | " priori_knowledge.add_forbidden_edges(\n",
484 | " [(all_vars[var_1], all_vars[var_2])]\n",
485 | " )\n",
486 | "\n",
487 | "print('\\nLLM knowledge vs true DAG')\n",
488 | "priori_dag = np.clip(priori_knowledge.matrix, 0, 1)\n",
489 | "\n",
490 | "print(f'\\nChecking if priori graph is a DAG: {check_if_dag(priori_dag)}')\n",
491 | "\n",
492 | "GraphDAG(\n",
493 | " est_dag=priori_dag, \n",
494 | " true_dag=true_dag)\n",
495 | "\n",
496 | "plt.show()\n",
497 | "\n",
498 | "print('\\nRunning PC')\n",
499 | "\n",
500 | "# Instantiate the model with expert knowledge\n",
501 | "pc_priori = PC(\n",
502 | " priori_knowledge=priori_knowledge,\n",
503 | " variant='stable'\n",
504 | ")\n",
505 | "\n",
506 | "# Learn\n",
507 | "pc_priori.learn(dataset)\n",
508 | "\n",
509 | "GraphDAG(\n",
510 | " est_dag=pc_priori.causal_matrix, \n",
511 | " true_dag=true_dag)\n",
512 | "\n",
513 | "plt.show()\n",
514 | "\n",
515 | "# Compute metrics\n",
516 | "metrics = MetricsDAG(\n",
517 | " B_est=pc_priori.causal_matrix, \n",
518 | " B_true=true_dag)\n",
519 | "\n",
520 | "print(metrics.metrics)"
521 | ]
522 | }
523 | ],
524 | "metadata": {
525 | "kernelspec": {
526 | "display_name": "Python [conda env:causal-nlp-openai-langchain]",
527 | "language": "python",
528 | "name": "conda-env-causal-nlp-openai-langchain-py"
529 | },
530 | "language_info": {
531 | "codemirror_mode": {
532 | "name": "ipython",
533 | "version": 3
534 | },
535 | "file_extension": ".py",
536 | "mimetype": "text/x-python",
537 | "name": "python",
538 | "nbconvert_exporter": "python",
539 | "pygments_lexer": "ipython3",
540 | "version": "3.9.17"
541 | }
542 | },
543 | "nbformat": 4,
544 | "nbformat_minor": 5
545 | }
546 |
--------------------------------------------------------------------------------
/Jane the Discoverer/causal-nlp-openai-langchain.yml:
--------------------------------------------------------------------------------
1 | name: causal-nlp-openai-langchain
2 | channels:
3 | - defaults
4 | - conda-forge
5 | - nvidia
6 | - pytorch
7 | dependencies:
8 | - python=3.9
9 | - pandas
10 | - xlrd
11 | - openpyxl
12 | - scikit-learn
13 | - matplotlib
14 | - networkx
15 | - seaborn
16 | - tqdm
17 | - nb_conda
18 | - ipywidgets
19 | - pytorch
20 | - pytorch-cuda=11.7
21 | - pip
22 | - pip:
23 | - openai
24 | - tiktoken
25 | - docarray
26 | - wikipedia
27 | - langchain==0.0.232
28 | - DateTime
29 | - dowhy==0.8
30 | - gcastle==1.0.3
31 |
--------------------------------------------------------------------------------
/Jane the Discoverer/img/CausalPython.io__flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Jane the Discoverer/img/CausalPython.io__flat.png
--------------------------------------------------------------------------------
/Jane the Discoverer/true_graph.png:
--------------------------------------------------------------------------------
1 | digraph {
2 | altitude
3 | oxygen_density
4 | temperature
5 | risk_of_death
6 | mehendretex
7 | altitude -> temperature
8 | }
9 |
--------------------------------------------------------------------------------
/Jane the Discoverer/true_graph.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlxndrMlk/blogs-code/89c8793c888473ad06baadec2c41771024f84e9c/Jane the Discoverer/true_graph.png.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Aleksander Molak
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Level Up - A Simple Blueprint to Predict Causal Effects for Unseen Data Using Python/CATE.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "4dbc6faa",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "import numpy as np\n",
11 | "import pandas as pd\n",
12 | "\n",
13 | "from econml.metalearners import SLearner, TLearner, XLearner\n",
14 | "from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor\n",
15 | "import lightgbm as lgb\n",
16 | "\n",
17 | "from sklearn.metrics import mean_absolute_percentage_error\n",
18 | "\n",
19 | "import networkx as nx\n",
20 | "\n",
21 | "import matplotlib.pyplot as plt\n",
22 | "plt.style.use('fivethirtyeight')"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": 2,
28 | "id": "779c727c",
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "COLORS = [\n",
33 | " '#FFFFFF',\n",
34 | " '#ECECEC',\n",
35 | " '#EBDF2B',\n",
36 | " '#263F6B',\n",
37 | " '#213559',\n",
38 | "]"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "id": "fc008629",
44 | "metadata": {},
45 | "source": [
46 | "# A Simple Blueprint to Predict Causal Effects for Unseen Data Using Python\n",
47 | "\n",
48 | "This is a notebook accompanying the blog post **[Level Up! A Simple Blueprint to Predict Causal Effects for Unseen Data Using Python.]()**"
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "id": "b6637201",
54 | "metadata": {},
55 | "source": [
56 | "**CATE** ($\\tau$) is defined as:\n",
57 | "\n",
58 | "$$\\Large \\tau(X_i) = E[Y_i(1)|X_i] - E[Y_i(0)|X_i]$$\n",
59 | "\n",
60 | " "
61 | ]
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "id": "899fb600",
66 | "metadata": {},
67 | "source": [
68 | "## Experimental data\n",
69 | "\n",
70 | "Our **SCM** is defined by the following set of structural equations:\n",
71 | "\n",
72 | "$$\\large X \\sim \\mathcal{N}(0, 1)$$\n",
73 | "\n",
74 | "$$\\large T \\sim Bernoulli(0.5)$$\n",
75 | "\n",
76 | "$$\\large y := 2\\times T \\times f(X) + 2 + \\mathcal{N}(0, 1)$$\n",
77 | "\n",
78 | " \n",
79 | "where:\n",
80 | "\n",
81 | "* $X$ is a $d$-dimensional feature vector (in our example $d=4$)\n",
82 | "* $T$ is the treatment\n",
83 | "* $y$ is the outcome\n",
84 | "\n",
85 | "* $f(X)$ is a function defined in the code block below.\n"
86 | ]
87 | },
88 | {
89 | "cell_type": "code",
90 | "execution_count": 3,
91 | "id": "53cf07bc",
92 | "metadata": {},
93 | "outputs": [],
94 | "source": [
95 | "G = nx.DiGraph()\n",
96 | "G.add_edges_from(\n",
97 | " [\n",
98 | " ('X', 'Y'),\n",
99 | " ('T', 'Y')\n",
100 | " ]\n",
101 | ")"
102 | ]
103 | },
104 | {
105 | "cell_type": "code",
106 | "execution_count": 4,
107 | "id": "62e5f4c8",
108 | "metadata": {},
109 | "outputs": [
110 | {
111 | "data": {
112 | "image/png": "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\n",
113 | "text/plain": [
114 | ""
115 | ]
116 | },
117 | "metadata": {},
118 | "output_type": "display_data"
119 | }
120 | ],
121 | "source": [
122 | "nx.draw(\n",
123 | " G, \n",
124 | " node_color=COLORS[4],\n",
125 | " node_size=1000,\n",
126 | " font_color=COLORS[0],\n",
127 | " with_labels=True,)"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": 28,
133 | "id": "c73c7f40",
134 | "metadata": {},
135 | "outputs": [],
136 | "source": [
137 | "class SCM:\n",
138 | " \"\"\"An SCM representing the data generating process\"\"\"\n",
139 | " def __init__(self, x_dim=1):\n",
140 | " self.x_dim = x_dim\n",
141 | " \n",
142 | " def sample(self, sample_size):\n",
143 | " X = np.random.normal(0, 1, (sample_size, self.x_dim))\n",
144 | " noise_y = np.random.randn(sample_size, 1)\n",
145 | " T = np.random.choice([0., 1.], (sample_size, 1))\n",
146 | " y = T * (2. * self.__get_coefficients(X)).sum(axis=1).reshape(-1, 1) + 2. + noise_y\n",
147 | " return X, T, y, noise_y\n",
148 | " \n",
149 | " def get_counterfactual(self, X, T, noise_y):\n",
150 | " y = T * (2. * self.__get_coefficients(X)).sum(axis=1).reshape(-1, 1) + 2. + noise_y\n",
151 | " return y\n",
152 | " \n",
153 | " def __get_coefficients(self, X):\n",
154 | " return np.sin(X) + X * np.where(X > 0, 2.25, -3.25)"
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "id": "252c260f",
160 | "metadata": {},
161 | "source": [
162 | "## Get the data"
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": 29,
168 | "id": "a475732f",
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "# Initialize the SCM\n",
173 | "scm = SCM(x_dim=4)\n",
174 | "\n",
175 | "# Sample from the SCM\n",
176 | "X, T, y, noise_y = scm.sample(1000)"
177 | ]
178 | },
179 | {
180 | "cell_type": "markdown",
181 | "id": "f2bed0a9",
182 | "metadata": {},
183 | "source": [
184 | "## Initialize and fit the models"
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": 30,
190 | "id": "3e177504",
191 | "metadata": {},
192 | "outputs": [],
193 | "source": [
194 | "# Intialize the models\n",
195 | "s_learner = SLearner(\n",
196 | " overall_model=lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
197 | ")\n",
198 | "\n",
199 | "t_learner = TLearner(\n",
200 | " models=[\n",
201 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10),\n",
202 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
203 | " ]\n",
204 | ")\n",
205 | "\n",
206 | "x_learner = XLearner(\n",
207 | " models=[\n",
208 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10),\n",
209 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
210 | " ],\n",
211 | " propensity_model=lgb.LGBMClassifier(n_estimators=500, max_depth=10)\n",
212 | ")"
213 | ]
214 | },
215 | {
216 | "cell_type": "code",
217 | "execution_count": 31,
218 | "id": "fec5be71",
219 | "metadata": {},
220 | "outputs": [
221 | {
222 | "name": "stderr",
223 | "output_type": "stream",
224 | "text": [
225 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
226 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
227 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n"
228 | ]
229 | },
230 | {
231 | "data": {
232 | "text/plain": [
233 | ""
234 | ]
235 | },
236 | "execution_count": 31,
237 | "metadata": {},
238 | "output_type": "execute_result"
239 | }
240 | ],
241 | "source": [
242 | "# Fit the models\n",
243 | "s_learner.fit(X=X, T=T.ravel(), Y=y.ravel())#, inference='bootstrap')\n",
244 | "t_learner.fit(X=X, T=T.ravel(), Y=y.ravel())#e='bootstrap')\n",
245 | "x_learner.fit(X=X, T=T.ravel(), Y=y.ravel())"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "id": "ac2c6657",
251 | "metadata": {},
252 | "source": [
253 | "## Evaluate the models"
254 | ]
255 | },
256 | {
257 | "cell_type": "code",
258 | "execution_count": 35,
259 | "id": "9ff578d3",
260 | "metadata": {},
261 | "outputs": [],
262 | "source": [
263 | "# Generate the test data\n",
264 | "X_test, _, y_test, noise_y_test = scm.sample(10000)\n",
265 | "\n",
266 | "results = []\n",
267 | "for i in range(1000):\n",
268 | " data_scm_1 = scm.get_counterfactual(X_test, np.ones(X_test.shape[0]).reshape(-1, 1), noise_y_test)\n",
269 | " data_scm_0 = scm.get_counterfactual(X_test, np.zeros(X_test.shape[0]).reshape(-1, 1), noise_y_test)\n",
270 | " results.append((data_scm_1 - data_scm_0))"
271 | ]
272 | },
273 | {
274 | "cell_type": "code",
275 | "execution_count": 36,
276 | "id": "51811693",
277 | "metadata": {},
278 | "outputs": [],
279 | "source": [
280 | "# Genertae predictions\n",
281 | "preds_s = s_learner.effect(X_test)\n",
282 | "preds_t = t_learner.effect(X_test)\n",
283 | "preds_x = x_learner.effect(X_test)\n",
284 | "\n",
285 | "# Generate the expected ground truth\n",
286 | "y_true_expected = np.array(results).mean(axis=0).ravel()"
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": 37,
292 | "id": "dc8636b7",
293 | "metadata": {},
294 | "outputs": [
295 | {
296 | "name": "stdout",
297 | "output_type": "stream",
298 | "text": [
299 | "S-Learner MAPE = 0.0895\n",
300 | "T-Learner MAPE = 0.0978\n",
301 | "X-Learner MAPE = 0.0974\n"
302 | ]
303 | }
304 | ],
305 | "source": [
306 | "# Compute MAPE\n",
307 | "for model, preds in {'S-Learner': preds_s, 'T-Learner': preds_t, 'X-Learner': preds_x}.items():\n",
308 | " mape = mean_absolute_percentage_error(y_true=y_true_expected, y_pred=preds)\n",
309 | " print(f'{model} MAPE = {mape:0.4f}')"
310 | ]
311 | },
312 | {
313 | "cell_type": "markdown",
314 | "id": "c68e3f04",
315 | "metadata": {},
316 | "source": [
317 | "## Confounded observational data"
318 | ]
319 | },
320 | {
321 | "cell_type": "code",
322 | "execution_count": 897,
323 | "id": "2a378786",
324 | "metadata": {},
325 | "outputs": [],
326 | "source": [
327 | "class SCM2:\n",
328 | " \"\"\"An SCM representing the data generating process\"\"\"\n",
329 | " def __init__(self, x_dim=1):\n",
330 | " self.x_dim = x_dim\n",
331 | " \n",
332 | " def sample(self, sample_size, p0=.5):\n",
333 | " self.Z = np.random.uniform(0, 1, (sample_size, 1))\n",
334 | " X = np.random.normal(0, 1, (sample_size, self.x_dim))\n",
335 | " T = np.where(self.Z < p0, 0, 1)#np.random.choice([0., 1.], (sample_size, 1), p=[])\n",
336 | " y = T * (2. * self.__get_coefficients(X)).sum(axis=1).reshape(-1, 1) + self.Z + 1. + np.random.randn(sample_size, 1)\n",
337 | " return X, T, y\n",
338 | " \n",
339 | " def get_counterfactual(self, X, T):\n",
340 | " y = T * (2. * self.__get_coefficients(X)).sum(axis=1).reshape(-1, 1) + self.Z + np.random.randn(X.shape[0], 1)\n",
341 | " return y\n",
342 | " \n",
343 | " def __get_coefficients(self, X):\n",
344 | " return np.sin(X) + X * np.where(X > 0, 2.25, -3.25)"
345 | ]
346 | },
347 | {
348 | "cell_type": "code",
349 | "execution_count": 946,
350 | "id": "eab4cf06",
351 | "metadata": {},
352 | "outputs": [],
353 | "source": [
354 | "# Initialize the SCM\n",
355 | "scm = SCM2(x_dim=4)\n",
356 | "\n",
357 | "# Sample from the SCM\n",
358 | "X, T, y = scm.sample(1000, p0=.71)"
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": 947,
364 | "id": "48bec4cc",
365 | "metadata": {},
366 | "outputs": [],
367 | "source": [
368 | "# Intialize the models\n",
369 | "s_learner = SLearner(\n",
370 | " overall_model=lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
371 | ")\n",
372 | "\n",
373 | "t_learner = TLearner(\n",
374 | " models=[\n",
375 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10),\n",
376 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
377 | " ]\n",
378 | ")\n",
379 | "\n",
380 | "x_learner = XLearner(\n",
381 | " models=[\n",
382 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10),\n",
383 | " lgb.LGBMRegressor(n_estimators=500, max_depth=10)\n",
384 | " ],\n",
385 | " propensity_model=lgb.LGBMClassifier(n_estimators=500, max_depth=10)\n",
386 | ")"
387 | ]
388 | },
389 | {
390 | "cell_type": "code",
391 | "execution_count": 948,
392 | "id": "f33198e5",
393 | "metadata": {},
394 | "outputs": [
395 | {
396 | "name": "stderr",
397 | "output_type": "stream",
398 | "text": [
399 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
400 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
401 | "Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n"
402 | ]
403 | },
404 | {
405 | "data": {
406 | "text/plain": [
407 | ""
408 | ]
409 | },
410 | "execution_count": 948,
411 | "metadata": {},
412 | "output_type": "execute_result"
413 | }
414 | ],
415 | "source": [
416 | "# Fit the models\n",
417 | "s_learner.fit(X=X, T=T.ravel(), Y=y.ravel())#, inference='bootstrap')\n",
418 | "t_learner.fit(X=X, T=T.ravel(), Y=y.ravel())#e='bootstrap')\n",
419 | "x_learner.fit(X=X, T=T.ravel(), Y=y.ravel())"
420 | ]
421 | },
422 | {
423 | "cell_type": "code",
424 | "execution_count": 949,
425 | "id": "0386b270",
426 | "metadata": {},
427 | "outputs": [],
428 | "source": [
429 | "# Generate the test data\n",
430 | "X_test, _, y_test = scm.sample(100)\n",
431 | "\n",
432 | "results = []\n",
433 | "for i in range(1000):\n",
434 | " data_scm_1 = scm.get_counterfactual(X_test, np.ones(X_test.shape[0]).reshape(-1, 1))\n",
435 | " data_scm_0 = scm.get_counterfactual(X_test, np.zeros(X_test.shape[0]).reshape(-1, 1))\n",
436 | " results.append((data_scm_1 - data_scm_0))"
437 | ]
438 | },
439 | {
440 | "cell_type": "code",
441 | "execution_count": 950,
442 | "id": "744b9943",
443 | "metadata": {},
444 | "outputs": [],
445 | "source": [
446 | "# Genertae predictions\n",
447 | "preds_s = s_learner.effect(X_test)\n",
448 | "preds_t = t_learner.effect(X_test)\n",
449 | "preds_x = x_learner.effect(X_test)\n",
450 | "\n",
451 | "# Generate the expected ground truth\n",
452 | "y_true_expected = np.array(results).mean(axis=0).ravel()"
453 | ]
454 | },
455 | {
456 | "cell_type": "code",
457 | "execution_count": 951,
458 | "id": "26865c21",
459 | "metadata": {},
460 | "outputs": [
461 | {
462 | "name": "stdout",
463 | "output_type": "stream",
464 | "text": [
465 | "S-Learner MAPE = 0.1155\n",
466 | "T-Learner MAPE = 0.1355\n",
467 | "X-Learner MAPE = 0.1305\n"
468 | ]
469 | }
470 | ],
471 | "source": [
472 | "# Compute MAPE\n",
473 | "for model, preds in {'S-Learner': preds_s, 'T-Learner': preds_t, 'X-Learner': preds_x}.items():\n",
474 | " mape = mean_absolute_percentage_error(y_true=y_true_expected, y_pred=preds)\n",
475 | " print(f'{model} MAPE = {mape:0.4f}')"
476 | ]
477 | },
478 | {
479 | "cell_type": "markdown",
480 | "id": "4a1d2b8e",
481 | "metadata": {},
482 | "source": [
483 | "## Hillstrom data"
484 | ]
485 | },
486 | {
487 | "cell_type": "code",
488 | "execution_count": 759,
489 | "id": "448ef2cc",
490 | "metadata": {},
491 | "outputs": [],
492 | "source": [
493 | "data = pd.read_csv(r'data/hillstrom_clean.csv')"
494 | ]
495 | },
496 | {
497 | "cell_type": "code",
498 | "execution_count": 760,
499 | "id": "f94251b7",
500 | "metadata": {},
501 | "outputs": [
502 | {
503 | "data": {
504 | "text/html": [
505 | "\n",
506 | "\n",
519 | "
\n",
520 | " \n",
521 | " \n",
522 | " \n",
523 | " recency \n",
524 | " history \n",
525 | " mens \n",
526 | " womens \n",
527 | " newbie \n",
528 | " visit \n",
529 | " conversion \n",
530 | " spend \n",
531 | " zip_code__rural \n",
532 | " zip_code__surburban \n",
533 | " zip_code__urban \n",
534 | " channel__multichannel \n",
535 | " channel__phone \n",
536 | " channel__web \n",
537 | " treatment \n",
538 | " \n",
539 | " \n",
540 | " \n",
541 | " \n",
542 | " 0 \n",
543 | " 10 \n",
544 | " 142.44 \n",
545 | " 1 \n",
546 | " 0 \n",
547 | " 0 \n",
548 | " 0 \n",
549 | " 0 \n",
550 | " 0.0 \n",
551 | " 0 \n",
552 | " 1 \n",
553 | " 0 \n",
554 | " 0 \n",
555 | " 1 \n",
556 | " 0 \n",
557 | " [0, 1, 0] \n",
558 | " \n",
559 | " \n",
560 | " 1 \n",
561 | " 6 \n",
562 | " 329.08 \n",
563 | " 1 \n",
564 | " 1 \n",
565 | " 1 \n",
566 | " 0 \n",
567 | " 0 \n",
568 | " 0.0 \n",
569 | " 1 \n",
570 | " 0 \n",
571 | " 0 \n",
572 | " 0 \n",
573 | " 0 \n",
574 | " 1 \n",
575 | " [1, 0, 0] \n",
576 | " \n",
577 | " \n",
578 | " 2 \n",
579 | " 7 \n",
580 | " 180.65 \n",
581 | " 0 \n",
582 | " 1 \n",
583 | " 1 \n",
584 | " 0 \n",
585 | " 0 \n",
586 | " 0.0 \n",
587 | " 0 \n",
588 | " 1 \n",
589 | " 0 \n",
590 | " 0 \n",
591 | " 0 \n",
592 | " 1 \n",
593 | " [0, 1, 0] \n",
594 | " \n",
595 | " \n",
596 | " 3 \n",
597 | " 9 \n",
598 | " 675.83 \n",
599 | " 1 \n",
600 | " 0 \n",
601 | " 1 \n",
602 | " 0 \n",
603 | " 0 \n",
604 | " 0.0 \n",
605 | " 1 \n",
606 | " 0 \n",
607 | " 0 \n",
608 | " 0 \n",
609 | " 0 \n",
610 | " 1 \n",
611 | " [0, 0, 1] \n",
612 | " \n",
613 | " \n",
614 | " 4 \n",
615 | " 2 \n",
616 | " 45.34 \n",
617 | " 1 \n",
618 | " 0 \n",
619 | " 0 \n",
620 | " 0 \n",
621 | " 0 \n",
622 | " 0.0 \n",
623 | " 0 \n",
624 | " 0 \n",
625 | " 1 \n",
626 | " 0 \n",
627 | " 0 \n",
628 | " 1 \n",
629 | " [0, 1, 0] \n",
630 | " \n",
631 | " \n",
632 | " ... \n",
633 | " ... \n",
634 | " ... \n",
635 | " ... \n",
636 | " ... \n",
637 | " ... \n",
638 | " ... \n",
639 | " ... \n",
640 | " ... \n",
641 | " ... \n",
642 | " ... \n",
643 | " ... \n",
644 | " ... \n",
645 | " ... \n",
646 | " ... \n",
647 | " ... \n",
648 | " \n",
649 | " \n",
650 | " 63995 \n",
651 | " 10 \n",
652 | " 105.54 \n",
653 | " 1 \n",
654 | " 0 \n",
655 | " 0 \n",
656 | " 0 \n",
657 | " 0 \n",
658 | " 0.0 \n",
659 | " 0 \n",
660 | " 0 \n",
661 | " 1 \n",
662 | " 0 \n",
663 | " 0 \n",
664 | " 1 \n",
665 | " [0, 0, 1] \n",
666 | " \n",
667 | " \n",
668 | " 63996 \n",
669 | " 5 \n",
670 | " 38.91 \n",
671 | " 0 \n",
672 | " 1 \n",
673 | " 1 \n",
674 | " 0 \n",
675 | " 0 \n",
676 | " 0.0 \n",
677 | " 0 \n",
678 | " 0 \n",
679 | " 1 \n",
680 | " 0 \n",
681 | " 1 \n",
682 | " 0 \n",
683 | " [0, 0, 1] \n",
684 | " \n",
685 | " \n",
686 | " 63997 \n",
687 | " 6 \n",
688 | " 29.99 \n",
689 | " 1 \n",
690 | " 0 \n",
691 | " 1 \n",
692 | " 0 \n",
693 | " 0 \n",
694 | " 0.0 \n",
695 | " 0 \n",
696 | " 0 \n",
697 | " 1 \n",
698 | " 0 \n",
699 | " 1 \n",
700 | " 0 \n",
701 | " [0, 0, 1] \n",
702 | " \n",
703 | " \n",
704 | " 63998 \n",
705 | " 1 \n",
706 | " 552.94 \n",
707 | " 1 \n",
708 | " 0 \n",
709 | " 1 \n",
710 | " 0 \n",
711 | " 0 \n",
712 | " 0.0 \n",
713 | " 0 \n",
714 | " 1 \n",
715 | " 0 \n",
716 | " 1 \n",
717 | " 0 \n",
718 | " 0 \n",
719 | " [0, 1, 0] \n",
720 | " \n",
721 | " \n",
722 | " 63999 \n",
723 | " 1 \n",
724 | " 472.82 \n",
725 | " 0 \n",
726 | " 1 \n",
727 | " 0 \n",
728 | " 0 \n",
729 | " 0 \n",
730 | " 0.0 \n",
731 | " 0 \n",
732 | " 1 \n",
733 | " 0 \n",
734 | " 0 \n",
735 | " 0 \n",
736 | " 1 \n",
737 | " [0, 0, 1] \n",
738 | " \n",
739 | " \n",
740 | "
\n",
741 | "
64000 rows × 15 columns
\n",
742 | "
"
743 | ],
744 | "text/plain": [
745 | " recency history mens womens newbie visit conversion spend \\\n",
746 | "0 10 142.44 1 0 0 0 0 0.0 \n",
747 | "1 6 329.08 1 1 1 0 0 0.0 \n",
748 | "2 7 180.65 0 1 1 0 0 0.0 \n",
749 | "3 9 675.83 1 0 1 0 0 0.0 \n",
750 | "4 2 45.34 1 0 0 0 0 0.0 \n",
751 | "... ... ... ... ... ... ... ... ... \n",
752 | "63995 10 105.54 1 0 0 0 0 0.0 \n",
753 | "63996 5 38.91 0 1 1 0 0 0.0 \n",
754 | "63997 6 29.99 1 0 1 0 0 0.0 \n",
755 | "63998 1 552.94 1 0 1 0 0 0.0 \n",
756 | "63999 1 472.82 0 1 0 0 0 0.0 \n",
757 | "\n",
758 | " zip_code__rural zip_code__surburban zip_code__urban \\\n",
759 | "0 0 1 0 \n",
760 | "1 1 0 0 \n",
761 | "2 0 1 0 \n",
762 | "3 1 0 0 \n",
763 | "4 0 0 1 \n",
764 | "... ... ... ... \n",
765 | "63995 0 0 1 \n",
766 | "63996 0 0 1 \n",
767 | "63997 0 0 1 \n",
768 | "63998 0 1 0 \n",
769 | "63999 0 1 0 \n",
770 | "\n",
771 | " channel__multichannel channel__phone channel__web treatment \n",
772 | "0 0 1 0 [0, 1, 0] \n",
773 | "1 0 0 1 [1, 0, 0] \n",
774 | "2 0 0 1 [0, 1, 0] \n",
775 | "3 0 0 1 [0, 0, 1] \n",
776 | "4 0 0 1 [0, 1, 0] \n",
777 | "... ... ... ... ... \n",
778 | "63995 0 0 1 [0, 0, 1] \n",
779 | "63996 0 1 0 [0, 0, 1] \n",
780 | "63997 0 1 0 [0, 0, 1] \n",
781 | "63998 1 0 0 [0, 1, 0] \n",
782 | "63999 0 0 1 [0, 0, 1] \n",
783 | "\n",
784 | "[64000 rows x 15 columns]"
785 | ]
786 | },
787 | "execution_count": 760,
788 | "metadata": {},
789 | "output_type": "execute_result"
790 | }
791 | ],
792 | "source": [
793 | "data"
794 | ]
795 | },
796 | {
797 | "cell_type": "code",
798 | "execution_count": null,
799 | "id": "e05ad153",
800 | "metadata": {},
801 | "outputs": [],
802 | "source": []
803 | }
804 | ],
805 | "metadata": {
806 | "kernelspec": {
807 | "display_name": "Python [conda env:econml-dowhy-py38]",
808 | "language": "python",
809 | "name": "conda-env-econml-dowhy-py38-py"
810 | },
811 | "language_info": {
812 | "codemirror_mode": {
813 | "name": "ipython",
814 | "version": 3
815 | },
816 | "file_extension": ".py",
817 | "mimetype": "text/x-python",
818 | "name": "python",
819 | "nbconvert_exporter": "python",
820 | "pygments_lexer": "ipython3",
821 | "version": "3.8.12"
822 | }
823 | },
824 | "nbformat": 4,
825 | "nbformat_minor": 5
826 | }
827 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blogging
2 | Code for blog posts.
3 |
--------------------------------------------------------------------------------