howardroark commited on
Commit
6f4f21f
1 Parent(s): beb8613

initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
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
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
161
+
162
+ data/
163
+ wandb/
README.md CHANGED
@@ -1,12 +1 @@
1
- ---
2
- title: Uplift Modeling
3
- emoji: 😻
4
- colorFrom: pink
5
- colorTo: green
6
- sdk: streamlit
7
- sdk_version: 1.32.2
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ # uplift_modeling
 
 
 
 
 
 
 
 
 
 
 
app.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from data_utils.data_generation import UpliftSimulation
2
+ from data_utils.exploratory_data_analysis import ExploratoryAnalysis
3
+ from data_utils.feature_importance import FeatureImportance
4
+ from models_utils.ml_models import ModelTraining
5
+ from eval_utils.evaluation import ModelEvaluator
6
+
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ import pandas as pd
10
+ import streamlit as st
11
+
12
+ X_names = [
13
+ 'AgeIndex', 'IncomeIndex', 'PurchaseFrequencyIndex',
14
+ 'AccountLifetimeIndex', 'AverageTransactionValueIndex', 'PreferredPaymentMethodIndex', 'RegionIndex',
15
+ 'EmailDiscountCTRIndex', 'WebDiscountCTRIndex', 'SocialMediaEngagementIndex',
16
+ 'DirectMailDiscountResponseIndex', 'InAppDiscountEngagementIndex', 'FlashSaleParticipationIndex',
17
+ 'SeasonalPromoInterestIndex', 'LoyaltyProgramEngagementIndex', 'ReferralBonusUsageIndex',
18
+ 'DiscountCodeRedemptionIndex', 'VIPSaleAccessIndex', 'EarlyAccessOptInIndex',
19
+ 'ProductReviewAfterDiscountIndex', 'UpsellConversionIndex', 'CrossSellInterestIndex',
20
+ 'BundlePurchaseIndex', 'SubscriptionUpgradeIndex', 'CustomerFeedbackIndex',
21
+ 'BrowserTypeIndex', 'DeviceCategoryIndex', 'OperatingSystemIndex',
22
+ 'SessionStartTimeIndex', 'LanguagePreferenceIndex', 'NewsletterSubscriptionIndex',
23
+ 'AccountVerificationStatusIndex', 'AdBlockerPresenceIndex'
24
+ ]
25
+
26
+ # Title
27
+ st.title("Uplift Modeling in Retail Demo")
28
+
29
+ tabs = st.sidebar.radio("Navigation", ["Data generation", "Exploratory analysis", "Model training", "Economic effects"])
30
+
31
+ if tabs == "Data generation":
32
+
33
+ st.header("Data Generation")
34
+
35
+ # Description
36
+ st.write("""
37
+ This app creates a simulated dataset for a special kind of analysis called uplift modeling, which helps understand the effect of different actions (like promotions) on customer behavior. We use some default settings to make things easy:
38
+ - We're looking at whether customers make a purchase or not.
39
+ - We compare different types of promotions (like no discount, 5% off, etc.).
40
+ - The dataset includes 15 different pieces of information (features) about each customer.
41
+ """)
42
+
43
+ # Interactive number of samples selection
44
+ n = st.number_input('Number of Samples (n)', min_value=1000, value=10000, step=1000,
45
+ help="Total number of samples to generate in the dataset.")
46
+
47
+ # Default values for other variables
48
+ y_name = 'conversion'
49
+ treatment_group_keys = ['control', 'discount_05', 'discount_10', 'discount_15']
50
+ n_classification_features = 15
51
+ n_classification_informative = 7
52
+ n_classification_repeated = 0
53
+ n_uplift_increase_dict = {'discount_05': 4, 'discount_10': 3, 'discount_15': 3}
54
+ n_uplift_decrease_dict = {'discount_05': 0, 'discount_10': 0, 'discount_15': 0}
55
+ positive_class_proportion = 0.05
56
+ random_seed = 8097
57
+
58
+ # Button to generate dataset
59
+ if st.button('Generate Dataset'):
60
+ uplift_sim = UpliftSimulation(n=n, y_name=y_name, treatment_group_keys=treatment_group_keys,
61
+ n_classification_features=n_classification_features,
62
+ n_classification_informative=n_classification_informative,
63
+ n_classification_repeated=n_classification_repeated,
64
+ n_uplift_increase_dict=n_uplift_increase_dict,
65
+ n_uplift_decrease_dict=n_uplift_decrease_dict,
66
+ positive_class_proportion=positive_class_proportion,
67
+ random_seed=random_seed)
68
+ uplift_sim.simulate_dataset()
69
+ uplift_sim.apply_discounts_and_clean()
70
+ uplift_sim.postprocess_tables()
71
+ uplift_sim.add_monetary_effect()
72
+ st.session_state.uplift_sim = uplift_sim # Store in session state
73
+
74
+ st.write("Dataset Generated Successfully!")
75
+
76
+ st.subheader("User profiles")
77
+ st.write('Features that represent a customer such as age, income, purchase frequency, etc')
78
+ st.dataframe(uplift_sim.dataframes[0].head(3))
79
+
80
+ st.subheader("Treatments data")
81
+ st.write('Information about the different treatments (discounts) that were applied to the customers as discounts in different channels (web, email, mobile), early access, etc')
82
+ st.dataframe(uplift_sim.dataframes[1].head(3))
83
+
84
+ st.subheader("Other data")
85
+ st.write('Other data that can be used in the analysis')
86
+ st.dataframe(uplift_sim.dataframes[2].head(3))
87
+
88
+ if tabs == "Exploratory analysis":
89
+
90
+ st.header("Exploratory Analysis")
91
+
92
+ if 'uplift_sim' in st.session_state:
93
+
94
+ st.subheader('Summary statistics')
95
+ uplift_sim = st.session_state.uplift_sim
96
+ eda = ExploratoryAnalysis(uplift_sim.df)
97
+
98
+ st.write('We begin by computing the total sum of conversions, sales (discounted price) and platform benefit. We can see that the total conversions and the total sales grows as the discount value is bigger. However, the platform benefit decreases.')
99
+
100
+ sum_conversions, mean_conversions = eda.compute_summaries()
101
+ st.write(sum_conversions)
102
+ st.write(mean_conversions)
103
+
104
+ st.write('We can also visualize the tradeoff between conversions and platform benefit by plotting the mean benefit per user on the y-axis and the mean conversion rate on the x-axis, for each treatment group.')
105
+ mean_benefit_vs_conversion = eda.compute_mean_benefit_vs_conversion()
106
+
107
+ fig, ax = plt.subplots()
108
+ mean_benefit_vs_conversion.plot.scatter(x='conversion', y='benefit', c='DarkBlue', s=50, ax=ax)
109
+ st.pyplot(fig)
110
+
111
+ st.write('''
112
+ We further compute the Average Treatment Effect (ATE) for both the mean conversion rate and the mean benefit per user:
113
+ - Conversion ATE = Mean Conversion rate in the discounted group minus Mean Conversion rate in the control group
114
+ - Benefit ATE = Mean Benefit per user in the discounted group minus Mean Benefit per user in the control group
115
+ This helps illustrate how the discount value affects Conversion ATE and Benefit ATE.
116
+ ''')
117
+ mean_conversions_ate = eda.compute_ate()
118
+
119
+ fig, ax = plt.subplots()
120
+ mean_conversions_ate.plot.scatter(x='conversion', y='benefit', c='DarkBlue', s=50, ax=ax)
121
+ st.pyplot(fig)
122
+
123
+ st.subheader('Feature importance')
124
+
125
+ # Allow users to select a treatment group
126
+ treatment_group = st.selectbox(
127
+ 'Select a treatment group',
128
+ options=['discount_05', 'discount_10', 'discount_15'],
129
+ index=0 # default to 'discount_05'
130
+ )
131
+
132
+ feature_importance = FeatureImportance(uplift_sim.df, X_names, y_name = 'conversion', treatment_group = treatment_group)
133
+ fi = feature_importance.compute_feature_importance()
134
+ fig, ax = plt.subplots()
135
+ di_df_sorted = fi.sort_values(by='score', ascending=False)
136
+ di_df_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
137
+ st.pyplot(fig)
138
+
139
+ st.write("""
140
+ - AccountLifetimeIndex: Longer-standing accounts are key predictors of customer response to promotions \n
141
+ - CustomerFeedbackIndex: Customer feedback significantly influences the success of marketing strategies \n
142
+ - UpsellConversionIndex: The success rate of upselling is an important factor \n
143
+ - PurchaseFrequencyIndex: More frequent purchases indicate higher engagement and response to marketing efforts \n
144
+ - ReferralBonusUsedIndex and LoyaltyProgramEngagementIndex: Engagement with these programs is highly indicative of responsiveness to promotions
145
+ """)
146
+
147
+ else:
148
+ st.error("Please generate the dataset first.")
149
+
150
+ if tabs == "Model training":
151
+
152
+ st.header("Model Training")
153
+
154
+ if 'uplift_sim' in st.session_state:
155
+
156
+ uplift_sim = st.session_state.uplift_sim
157
+
158
+ model_trainer = ModelTraining(uplift_sim.df, 'conversion', X_names)
159
+
160
+ model_type = st.radio("Choose the model type", ('Conversion Model', 'Benefit Model'))
161
+
162
+ params = {
163
+ 'n_estimators': st.slider('Number of Estimators', 10, 100, 50),
164
+ 'max_depth': st.slider('Max Depth', 1, 10, 4),
165
+ 'colsample_bytree': st.slider('Colsample by Tree', 0.1, 1.0, 0.2),
166
+ 'subsample': st.slider('Subsample', 0.1, 1.0, 0.2),
167
+ }
168
+ control_name = 'control' # st.text_input('Control Group Name', 'control')
169
+ test_size = st.slider('Test Size', 0.1, 0.9, 0.5)
170
+ random_state = 20143 # st.slider('Random State', 0, 10000, 20143)
171
+
172
+ if st.button('Train Model'):
173
+
174
+ model_trainer.split_data(test_size=test_size, random_state=random_state)
175
+
176
+ if model_type == 'Conversion Model':
177
+ y_name = 'conversion' # st.selectbox('Select target variable for conversion', options=uplift_sim.target_options)
178
+ model_trainer.y_name = y_name
179
+ tau = model_trainer.fit_predict_classifier(params, control_name)
180
+ elif model_type == 'BATE Model':
181
+ y_name = 'benefit' # st.selectbox('Select target variable for benefit', options=uplift_sim.benefit_options)
182
+ model_trainer.y_name = y_name
183
+ tau = model_trainer.fit_predict_regressor(params, control_name)
184
+
185
+ st.session_state.model_trainer = model_trainer
186
+
187
+ feature_importances = model_trainer.compute_feature_importance()
188
+
189
+ st.subheader('Feature Importances')
190
+ fig, ax = plt.subplots()
191
+
192
+ for k, v in feature_importances.items():
193
+ st.write(f"Feature importance for {k}")
194
+ v.plot(kind='barh', ax=ax)
195
+ ax.set_xlabel("Importance")
196
+ ax.set_ylabel("Feature")
197
+ ax.set_title(f"Feature Importance for {model_type}")
198
+ st.pyplot(fig)
199
+
200
+ else:
201
+ st.error("Please generate and preprocess the dataset first.")
202
+
203
+ if tabs == "Economic effects":
204
+
205
+ st.header("Economic Effects Analysis")
206
+
207
+ if 'uplift_sim' in st.session_state and 'model_trainer' in st.session_state:
208
+ df_test = st.session_state.model_trainer.df_test
209
+ model_type = st.radio("Choose the model type for analysis", ('Conversion Model', 'Benefit Model'))
210
+
211
+ # Determine which model to use based on user selection
212
+ if model_type == 'Conversion Model':
213
+ model = st.session_state.model_trainer.conversion_learner_t
214
+ elif model_type == 'Benefit Model':
215
+ model = st.session_state.model_trainer.benefit_learner_t
216
+ else:
217
+ st.error("Invalid model type selected.")
218
+ st.stop()
219
+
220
+ if model == None:
221
+ st.error("Please train the model first.")
222
+ st.stop()
223
+
224
+ evaluator = ModelEvaluator(model,
225
+ df_test,
226
+ X_names # df_test.columns.drop(['conversion', 'benefit', 'treatment_group_key'])
227
+ )
228
+ discounts = ['discount_05', 'discount_10', 'discount_15']
229
+ qini_conversions = {}
230
+ qini_benefits = {}
231
+
232
+ for discount in discounts:
233
+ qini_conv, qini_ben = evaluator.eval_performance(discount)
234
+ qini_conversions[discount] = qini_conv
235
+ qini_benefits[discount] = qini_ben
236
+
237
+ # Plotting CATE Conversion
238
+ st.subheader("CATE Conversion vs Targeted Population")
239
+ fig, ax_conversion = plt.subplots()
240
+ for discount, color in zip(discounts, ['b', 'g', 'y']):
241
+ qini_conversions[discount].plot(ax=ax_conversion, x='index', y='S', color=color)
242
+ qini_conversions[discount].plot(ax=ax_conversion, x='index', y='Random', color='r', ls='--')
243
+
244
+ ax_conversion.legend([f'{d} model' for d in discounts] + [f'{d} random' for d in discounts], prop={'size': 10})
245
+ ax_conversion.set_xlabel('Fraction of Targeted Users')
246
+ ax_conversion.set_ylabel('CATE Conversion')
247
+ ax_conversion.set_title('CATE Conversion vs Targeted Population')
248
+ st.pyplot(fig)
249
+
250
+ # Plotting CATE Benefit
251
+ st.subheader("CATE Benefit vs Targeted Population")
252
+ fig, ax_benefit = plt.subplots()
253
+ for discount, color in zip(discounts, ['b', 'g', 'y']):
254
+ qini_benefits[discount].plot(ax=ax_benefit, x='index', y='S', color=color)
255
+ qini_benefits[discount].plot(ax=ax_benefit, x='index', y='Random', color='r', ls='--')
256
+
257
+ ax_benefit.legend([f'{d} model' for d in discounts] + [f'{d} random' for d in discounts], prop={'size': 10})
258
+ ax_benefit.set_xlabel('Fraction of Targeted Users')
259
+ ax_benefit.set_ylabel('CATE Benefit')
260
+ ax_benefit.set_title('CATE Benefit vs Targeted Population')
261
+ st.pyplot(fig)
262
+
263
+ # Plotting CATE Benefit vs CATE Conversion
264
+ st.subheader("CATE Benefit vs CATE Conversion")
265
+ fig, ax_comp = plt.subplots()
266
+ colors = ['b', 'g', 'y']
267
+ for i, discount in enumerate(discounts):
268
+ qini_conc_test = pd.concat([qini_conversions[discount][['S']], qini_benefits[discount][['S']]], axis=1)
269
+ qini_conc_test.columns = ['cate_conversion', 'cate_benefit']
270
+ qini_conc_test.plot(ax=ax_comp, x='cate_conversion', y='cate_benefit', color=colors[i], label=f'{discount} model')
271
+
272
+ ax_comp.legend(prop={'size': 10})
273
+ ax_comp.set_xlabel('CATE Conversion')
274
+ ax_comp.set_ylabel('CATE Benefit')
275
+ ax_comp.set_title('CATE Benefit vs CATE Conversion')
276
+ st.pyplot(fig)
277
+
278
+ else:
279
+ st.error("Please ensure the model is trained and the dataset is prepared.")
app_old.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+
3
+ import pandas as pd
4
+ import streamlit as st
5
+ import matplotlib.pyplot as plt
6
+
7
+ from data_utils.data_simulation import UpliftSimulationReady
8
+ from data_utils.eda_simulation import EDASimulationReady
9
+ from data_utils.feature_importance_simulation import FISimulationReady
10
+ from models_utils.models_simulation import CATESimulationReady
11
+ from eval_utils.evaluation_simulation import CATEConversionEvaluateSimulationReady, CATEBenefitEvaluateSimulationReady
12
+
13
+ from mlops_utils.wandb_utils import upload_dataset_to_wandb, eda_work_with_dataset_to_wandb, training_results_to_wandb
14
+
15
+ st.title('Causal Uplift Modeling')
16
+ tabs = st.sidebar.radio("Navigation", ["Data", "EDA", "Modeling", "Effect"])
17
+
18
+ if tabs == "Data":
19
+
20
+ # Needed raw data
21
+ uplift_simulation = UpliftSimulationReady('./data/raw_data_client/')
22
+ user_profiles = uplift_simulation.load_user_profiles('user_profiles.csv')
23
+ uplift_data = uplift_simulation.load_uplift_data('uplift_data.csv')
24
+ irrelevant_data = uplift_simulation.load_irrelevant_data('irrelevant_data.csv')
25
+ transaction_data = uplift_simulation.load_other_data('transaction_data.csv')
26
+
27
+ # Subtitle
28
+ st.subheader('Loading data')
29
+
30
+ st.write('User profiles')
31
+ st.write(user_profiles.head(5))
32
+
33
+ st.write('Uplift data')
34
+ st.write(uplift_data.head(5))
35
+
36
+ st.write('Other data')
37
+ st.write(irrelevant_data.head(5))
38
+
39
+ st.write('Transaction data')
40
+ st.write(transaction_data.head(5))
41
+
42
+ if st.button('Upload data to wandb'):
43
+ upload_dataset_to_wandb(['./data/raw_data_client'], 'nl_cate_modeling', 'uplift_data')
44
+ st.write('Data uploaded to wandb')
45
+
46
+ # TODO: add to WANDB data processing step in the beginning
47
+ # TODO: the tree of updates
48
+ # TODO: choose the version from MLOps here exactly
49
+
50
+ if tabs == "EDA":
51
+
52
+ eda_simulation = EDASimulationReady('./data/processed_data/')
53
+ sum_conversions, mean_conversions = eda_simulation.load_conversions('uplift_classification_processed.csv')
54
+
55
+ st.subheader('Exploratory Data Analysis')
56
+
57
+ st.write('We can begin by computing the total sum of conversions, sales (discounted price) and platform benefit. We can see that the total conversions and the total sales grows as the discount value is bigger. However the platform benefit decreases.')
58
+ st.write(sum_conversions)
59
+
60
+ st.write('We can repeat the analysis but using the mean instead of the sum. This will give us the mean conversion rate, the mean sales per user and the mean platform benefit per user.')
61
+ st.write(mean_conversions)
62
+
63
+ st.write('To illustrate the tradeoff between conversions and platform benefit we can plot the mean benefit per user in the y-axis and the mean conversion rate in the x-axis, per treatment group.')
64
+
65
+ df_pivot_mean = mean_conversions[['mean']]
66
+ df_pivot_mean.columns = df_pivot_mean.columns.droplevel()
67
+
68
+ fig, ax = plt.subplots()
69
+ df_pivot_mean.plot.scatter(x='conversion',
70
+ y='benefit',
71
+ c='DarkBlue',
72
+ s=50,
73
+ ax=ax)
74
+ st.pyplot(fig)
75
+
76
+ st.write('''
77
+ We can also compute the Average Treatment Effect (ATE) for both the mean conversion rate and the mean benefit per user:
78
+ Conversion ATE = Mean Converstion rate in discounted group minus Mean Conversion rate in control group
79
+ Benefit ATE = Mean Benefit per user in discounted group minus Mean Benefit per user in control group
80
+ We can see in the plot below that the bigger the discount value the stronger the Conversion ATE (x-axis), but at the same time the more negative the Benefit ATE (y-axis).
81
+ ''')
82
+
83
+ df_pivot_mean_ate = df_pivot_mean - df_pivot_mean.loc['control'].values.squeeze()
84
+ df_pivot_mean_ate.columns = ['benefit_ate', 'conversion_ate', 'discounted_price_ate']
85
+
86
+ fig, ax = plt.subplots()
87
+ df_pivot_mean_ate.plot.scatter(x='conversion_ate',
88
+ y='benefit_ate',
89
+ c='DarkBlue',
90
+ s=50,
91
+ ax=ax)
92
+ st.pyplot(fig)
93
+
94
+ st.subheader('Feature Importance')
95
+
96
+ fi = FISimulationReady('./data/eda_data/')
97
+ di_df = fi.load_feature_importance('kl_feature_importance.csv')
98
+
99
+ st.write('Feature importance')
100
+ fig, ax = plt.subplots()
101
+ di_df_sorted = di_df.sort_values(by='score', ascending=False)
102
+ di_df_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
103
+ st.pyplot(fig)
104
+
105
+ if st.button('Upload EDA to wandb'):
106
+ eda_work_with_dataset_to_wandb(
107
+ dirs = ['./data/eda_data/'],
108
+ project_name = 'nl_cate_modeling',
109
+ dataset_name = 'uplift_data:latest',
110
+ dataset_type = 'raw_dataset',
111
+ artifact_type = 'eda')
112
+ st.write('EDA uploaded to wandb')
113
+
114
+ # TODO: add report to WANDB
115
+ # TODO: add artifacts to WANDB
116
+
117
+ if tabs == "Modeling":
118
+
119
+ st.subheader('Causal ML modeling')
120
+
121
+ st.write('We can begin by modeling the Conditional Average Treatment Effect')
122
+ if st.button('Train & run CATE conversion model'):
123
+ # fake trainin via 5 seconds spinner
124
+ with st.spinner('Training model...'):
125
+ time.sleep(2)
126
+
127
+ st.subheader('Feature importance by discount group')
128
+
129
+ model = CATESimulationReady('./data/models_data/model.pkl', './data/models_data/y_pred.pkl')
130
+ y_pred = model.predict()
131
+
132
+ fi05 = model.feature_importance('./data/models_data/discount_05_feature_importance.csv')
133
+ fi10 = model.feature_importance('./data/models_data/discount_10_feature_importance.csv')
134
+ fi15 = model.feature_importance('./data/models_data/discount_15_feature_importance.csv')
135
+
136
+ st.write('5\% discount group')
137
+ # plot feature importance as bar chart
138
+ fig, ax = plt.subplots()
139
+ fi05_sorted = fi05.sort_values(by='score', ascending=False)
140
+ fi05_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
141
+ st.pyplot(fig)
142
+
143
+ st.write('10\% discount group')
144
+ fig, ax = plt.subplots()
145
+ fi10_sorted = fi10.sort_values(by='score', ascending=False)
146
+ fi10_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
147
+ st.pyplot(fig)
148
+
149
+ st.write('15\% discount group')
150
+ fig, ax = plt.subplots()
151
+ fi15_sorted = fi15.sort_values(by='score', ascending=False)
152
+ fi15_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
153
+ st.pyplot(fig)
154
+ if st.button('Upload convesion model to wandb'):
155
+ training_results_to_wandb(['./data/models_data'],
156
+ 'nl_cate_modeling',
157
+ 'uplift_data:latest',
158
+ 'raw_dataset',
159
+ 'model_artifacts',
160
+ 'causal_model_conversion')
161
+ st.write('Models uploaded to wandb')
162
+
163
+ st.write('Similarly we can now train a T-Learner on the benefit label, and use the model predictions to evaluate the performance on the CATE conversion and CATE benefit.')
164
+ if st.button('Train & run CATE benefit model'):
165
+ # fake trainin via 5 seconds spinner
166
+ with st.spinner('Training model...'):
167
+ time.sleep(2)
168
+
169
+ st.subheader('Feature importance by discount group')
170
+
171
+ model = CATESimulationReady('./data/models_data/model.pkl', './data/models_data/y_pred.pkl')
172
+ y_pred = model.predict()
173
+
174
+ fi05 = model.feature_importance('./data/models_data/discount_05_feature_importance_bate.csv')
175
+ fi10 = model.feature_importance('./data/models_data/discount_10_feature_importance_bate.csv')
176
+ fi15 = model.feature_importance('./data/models_data/discount_15_feature_importance_bate.csv')
177
+
178
+ st.write('5\% discount group')
179
+ # plot feature importance as bar chart
180
+ fig, ax = plt.subplots()
181
+ fi05_sorted = fi05.sort_values(by='score', ascending=False)
182
+ fi05_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
183
+ st.pyplot(fig)
184
+
185
+ st.write('10\% discount group')
186
+ fig, ax = plt.subplots()
187
+ fi10_sorted = fi10.sort_values(by='score', ascending=False)
188
+ fi10_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
189
+ st.pyplot(fig)
190
+
191
+ st.write('15\% discount group')
192
+ fig, ax = plt.subplots()
193
+ fi15_sorted = fi15.sort_values(by='score', ascending=False)
194
+ fi15_sorted[['feature', 'score']].plot.barh(x='feature', y='score', ax=ax)
195
+ st.pyplot(fig)
196
+ if st.button('Upload benefit model to wandb'):
197
+ training_results_to_wandb(['./data/models_data'],
198
+ 'nl_cate_modeling',
199
+ 'uplift_data:latest',
200
+ 'raw_dataset',
201
+ 'model_artifacts',
202
+ 'causal_model_benefit')
203
+ st.write('Models uploaded to wandb')
204
+
205
+ if tabs == "Effect":
206
+
207
+ st.subheader('Causal ML evaluation')
208
+ st.write('We can evaluate our models by looking at the Qini curves. We can use the CATE conversion model to evaluate the performance on both the Conversion and the Benefit as a function of the fraction of users targeted.')
209
+
210
+ # two columns
211
+ col1, col2 = st.columns(2)
212
+
213
+ with col1:
214
+
215
+ st.write('CATE conversion model')
216
+
217
+ eval = CATEConversionEvaluateSimulationReady('./data/effect_data/')
218
+ qini_05_conversion_test, qini_05_benefit_test = eval.evaluate(5)
219
+ qini_10_conversion_test, qini_10_benefit_test = eval.evaluate(10)
220
+ qini_15_conversion_test, qini_15_benefit_test = eval.evaluate(15)
221
+
222
+ # Plot CATE conversion vs Targeted Population
223
+ fig_conversion, ax_conversion = plt.subplots()
224
+ qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')
225
+ qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')
226
+ qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')
227
+ qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='b', label = '5% model')
228
+ qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='g', label = '10% model')
229
+ qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='y', label = '15% model')
230
+ ax_conversion.legend()
231
+ ax_conversion.set_xlabel('Fraction of Targeted Users')
232
+ ax_conversion.set_ylabel('CATE conversion')
233
+ ax_conversion.set_title('CATE conversion vs Targeted Population')
234
+ st.pyplot(fig_conversion)
235
+
236
+ # Plot CATE benefit vs Targeted Population
237
+ fig_benefit, ax_benefit = plt.subplots()
238
+ qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')
239
+ qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')
240
+ qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')
241
+ qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='b', label = '5% model')
242
+ qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='g', label = '10% model')
243
+ qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='y', label = '15% model')
244
+ ax_benefit.legend()
245
+ ax_benefit.set_xlabel('Fraction of Targeted Users')
246
+ ax_benefit.set_ylabel('CATE Benefit')
247
+ ax_benefit.set_title('CATE benefit vs Targeted Population')
248
+ st.pyplot(fig_benefit)
249
+
250
+ qini_05_conc_test = pd.concat([qini_05_conversion_test[['S']], qini_05_benefit_test[['S']]], axis=1)
251
+ qini_05_conc_test.columns = ['cate_conversion', 'cate_benefit']
252
+ qini_10_conc_test = pd.concat([qini_10_conversion_test[['S']], qini_10_benefit_test[['S']]], axis=1)
253
+ qini_10_conc_test.columns = ['cate_conversion', 'cate_benefit']
254
+ qini_15_conc_test = pd.concat([qini_15_conversion_test[['S']], qini_15_benefit_test[['S']]], axis=1)
255
+ qini_15_conc_test.columns = ['cate_conversion', 'cate_benefit']
256
+
257
+ fig_conversion, ax_conversion = plt.subplots()
258
+ qini_05_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='b')
259
+ qini_10_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='g')
260
+ qini_15_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='y')
261
+ ax_conversion.legend(['5% model', '10% model','15% model'], prop={'size': 10})
262
+ ax_conversion.set_xlabel('CATE Conversion')
263
+ ax_conversion.set_ylabel('CATE Benefit')
264
+ ax_conversion.set_title('CATE benefit vs CATE conversion')
265
+ st.pyplot(fig_conversion)
266
+
267
+ if st.button('Upload conversion effects to wandb'):
268
+ training_results_to_wandb(['./data/effect_data'],
269
+ 'nl_cate_modeling',
270
+ 'causal_model_conversion:latest',
271
+ 'model_artifacts',
272
+ 'effects_artifacts',
273
+ 'convesion_model_evaluation',
274
+ job_type='evaluation')
275
+ st.write('Evaluation uploaded to wandb')
276
+
277
+ with col2:
278
+ st.write('CATE benefit model')
279
+
280
+ eval = CATEBenefitEvaluateSimulationReady('./data/effect_data/')
281
+ qini_05_conversion_test, qini_05_benefit_test = eval.evaluate(5)
282
+ qini_10_conversion_test, qini_10_benefit_test = eval.evaluate(10)
283
+ qini_15_conversion_test, qini_15_benefit_test = eval.evaluate(15)
284
+
285
+ # Plot CATE conversion vs Targeted Population
286
+ fig_conversion, ax_conversion = plt.subplots()
287
+ qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')
288
+ qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')
289
+ qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')
290
+ qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='b', label = '5% model')
291
+ qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='g', label = '10% model')
292
+ qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='y', label = '15% model')
293
+ ax_conversion.legend()
294
+ ax_conversion.set_xlabel('Fraction of Targeted Users')
295
+ ax_conversion.set_ylabel('CATE conversion')
296
+ ax_conversion.set_title('CATE conversion vs Targeted Population')
297
+ st.pyplot(fig_conversion)
298
+
299
+ # Plot CATE benefit vs Targeted Population
300
+ fig_benefit, ax_benefit = plt.subplots()
301
+ qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')
302
+ qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')
303
+ qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')
304
+ qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='b', label = '5% model')
305
+ qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='g', label = '10% model')
306
+ qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='y', label = '15% model')
307
+ ax_benefit.legend()
308
+ ax_benefit.set_xlabel('Fraction of Targeted Users')
309
+ ax_benefit.set_ylabel('CATE Benefit')
310
+ ax_benefit.set_title('CATE benefit vs Targeted Population')
311
+ st.pyplot(fig_benefit)
312
+
313
+ qini_05_conc_test = pd.concat([qini_05_conversion_test[['S']], qini_05_benefit_test[['S']]], axis=1)
314
+ qini_05_conc_test.columns = ['cate_conversion', 'cate_benefit']
315
+ qini_10_conc_test = pd.concat([qini_10_conversion_test[['S']], qini_10_benefit_test[['S']]], axis=1)
316
+ qini_10_conc_test.columns = ['cate_conversion', 'cate_benefit']
317
+ qini_15_conc_test = pd.concat([qini_15_conversion_test[['S']], qini_15_benefit_test[['S']]], axis=1)
318
+ qini_15_conc_test.columns = ['cate_conversion', 'cate_benefit']
319
+
320
+ fig_conversion, ax_conversion = plt.subplots()
321
+ qini_05_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='b')
322
+ qini_10_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='g')
323
+ qini_15_conc_test.plot(ax=ax_conversion, x='cate_conversion',y='cate_benefit',color='y')
324
+ ax_conversion.legend(['5% model', '10% model','15% model'], prop={'size': 10})
325
+ ax_conversion.set_xlabel('CATE Conversion')
326
+ ax_conversion.set_ylabel('CATE Benefit')
327
+ ax_conversion.set_title('CATE benefit vs CATE conversion')
328
+ st.pyplot(fig_conversion)
329
+
330
+ if st.button('Upload benefit effects to wandb'):
331
+ training_results_to_wandb(['./data/effect_data'],
332
+ 'nl_cate_modeling',
333
+ 'causal_model_benefit:latest',
334
+ 'model_artifacts',
335
+ 'effects_artifacts',
336
+ 'benefit_model_evaluation',
337
+ job_type='evaluation')
338
+ st.write('Evaluation uploaded to wandb')
339
+
340
+ st.write('To simplify the comparison, we can plot the CATE Benefit as a function of the CATE conversion.')
341
+ st.write('In the last plot for example we can see that there is a region where offering 15% discount to a targeted group of users is more efficient than giving 10% to everyone. We can obtain the same impact in overall conversion uplift while reducing our benefit loss considerably.')
data_utils/data_generation.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from functools import reduce
3
+ from random import randint
4
+
5
+ from causalml.dataset import make_uplift_classification
6
+
7
+ class UpliftSimulation:
8
+
9
+ def __init__(self, n=50000, y_name='conversion',
10
+ treatment_group_keys=['control', 'discount_05', 'discount_10', 'discount_15'],
11
+ n_classification_features=15, n_classification_informative=7,
12
+ n_classification_repeated=0,
13
+ n_uplift_increase_dict={'discount_05': 4, 'discount_10': 3, 'discount_15': 3},
14
+ n_uplift_decrease_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0},
15
+ delta_uplift_increase_dict={'discount_05': 0.0020, 'discount_10': 0.0045, 'discount_15': 0.008},
16
+ delta_uplift_decrease_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0},
17
+ n_uplift_increase_mix_informative_dict={'discount_05': 3, 'discount_10': 2, 'discount_15': 3},
18
+ n_uplift_decrease_mix_informative_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0},
19
+ positive_class_proportion=0.05, random_seed=8097):
20
+ self.n = n
21
+ self.y_name = y_name
22
+ self.treatment_group_keys = treatment_group_keys
23
+ self.n_classification_features = n_classification_features
24
+ self.n_classification_informative = n_classification_informative
25
+ self.n_classification_repeated = n_classification_repeated
26
+ self.n_uplift_increase_dict = n_uplift_increase_dict
27
+ self.n_uplift_decrease_dict = n_uplift_decrease_dict
28
+ self.delta_uplift_increase_dict = delta_uplift_increase_dict
29
+ self.delta_uplift_decrease_dict = delta_uplift_decrease_dict
30
+ self.n_uplift_increase_mix_informative_dict = n_uplift_increase_mix_informative_dict
31
+ self.n_uplift_decrease_mix_informative_dict = n_uplift_decrease_mix_informative_dict
32
+ self.positive_class_proportion = positive_class_proportion
33
+ self.random_seed = random_seed
34
+ self.df = None
35
+ self.X_names = None
36
+
37
+ def simulate_dataset(self):
38
+ self.df, self.X_names = make_uplift_classification(
39
+ treatment_name=self.treatment_group_keys,
40
+ y_name=self.y_name,
41
+ n_samples=self.n,
42
+ n_classification_features=self.n_classification_features,
43
+ n_classification_informative=self.n_classification_informative,
44
+ n_classification_repeated=self.n_classification_repeated,
45
+ n_uplift_increase_dict=self.n_uplift_increase_dict,
46
+ n_uplift_decrease_dict=self.n_uplift_decrease_dict,
47
+ delta_uplift_increase_dict=self.delta_uplift_increase_dict,
48
+ delta_uplift_decrease_dict=self.delta_uplift_decrease_dict,
49
+ n_uplift_increase_mix_informative_dict=self.n_uplift_increase_mix_informative_dict,
50
+ n_uplift_decrease_mix_informative_dict=self.n_uplift_decrease_mix_informative_dict,
51
+ positive_class_proportion=self.positive_class_proportion,
52
+ random_seed=self.random_seed,
53
+ )
54
+
55
+ def apply_discounts_and_clean(self):
56
+ discounts_dict = {'control': 0, 'discount_05': 0.05, 'discount_10': 0.10, 'discount_15': 0.15}
57
+ self.df['discount'] = self.df['treatment_group_key']
58
+ self.df = self.df.replace({"discount": discounts_dict})
59
+ self.df.drop(columns=['treatment_effect'], inplace=True)
60
+
61
+
62
+ def postprocess_tables(self):
63
+
64
+ # Add a synthetic UserID for each entry
65
+ self.df['UserID'] = range(len(self.df))
66
+
67
+ # Mapping the columns
68
+ informative_cols = [col for col in self.df.columns if 'informative' in col]
69
+ uplift_cols = [col for col in self.df.columns if 'uplift' in col]
70
+ irrelevant_cols = [col for col in self.df.columns if 'irrelevant' in col]
71
+ transaction_cols = ['treatment_group_key', 'conversion', 'discount']
72
+
73
+ # User Demographics and Profiles Table (Including Informative Features)
74
+ user_profiles = self.df[['UserID'] + informative_cols].copy()
75
+
76
+ # Web Interaction Data Table (This might need adjustment based on actual data)
77
+ # If any of the 'informative' columns relate to web interaction, include them here.
78
+
79
+ # Uplift-Related Data Table
80
+ uplift_data = self.df[['UserID'] + uplift_cols].copy()
81
+
82
+ # Adjusting the Uplift-Related Data table to include the mixed features
83
+ mixed_uplift_columns = ['x31_increase_mix', 'x22_increase_mix', 'x20_increase_mix',
84
+ 'x33_increase_mix', 'x32_increase_mix', 'x27_increase_mix',
85
+ 'x21_increase_mix', 'x26_increase_mix']
86
+
87
+ # Assuming uplift_data already includes the 'UserID' column
88
+ uplift_data = pd.concat([uplift_data, self.df[mixed_uplift_columns]], axis=1)
89
+
90
+ # Irrelevant Data Table
91
+ irrelevant_data = self.df[['UserID'] + irrelevant_cols].copy()
92
+
93
+ # Transaction Data Table
94
+ transaction_data = self.df[['UserID'] + transaction_cols].copy()
95
+
96
+ user_profiles.columns = [
97
+ 'UserID', 'AgeIndex', 'IncomeIndex', 'PurchaseFrequencyIndex',
98
+ 'AccountLifetimeIndex', 'AverageTransactionValueIndex', 'PreferredPaymentMethodIndex', 'RegionIndex'
99
+ ]
100
+
101
+ uplift_data.columns = [
102
+ 'UserID', 'EmailDiscountCTRIndex', 'WebDiscountCTRIndex', 'SocialMediaEngagementIndex',
103
+ 'DirectMailDiscountResponseIndex', 'InAppDiscountEngagementIndex', 'FlashSaleParticipationIndex',
104
+ 'SeasonalPromoInterestIndex', 'LoyaltyProgramEngagementIndex', 'ReferralBonusUsageIndex',
105
+ 'DiscountCodeRedemptionIndex', 'VIPSaleAccessIndex', 'EarlyAccessOptInIndex',
106
+ 'ProductReviewAfterDiscountIndex', 'UpsellConversionIndex', 'CrossSellInterestIndex',
107
+ 'BundlePurchaseIndex', 'SubscriptionUpgradeIndex', 'CustomerFeedbackIndex'
108
+ ]
109
+
110
+ irrelevant_data.columns = [
111
+ 'UserID', 'BrowserTypeIndex', 'DeviceCategoryIndex', 'OperatingSystemIndex',
112
+ 'SessionStartTimeIndex', 'LanguagePreferenceIndex', 'NewsletterSubscriptionIndex',
113
+ 'AccountVerificationStatusIndex', 'AdBlockerPresenceIndex'
114
+ ]
115
+
116
+ # transaction_data.columns = [
117
+ # 'UserID', 'DiscountCategoryIndex', 'PurchaseIndex', 'DiscountPercentageIndex'
118
+ # ]
119
+ transaction_data.columns = ['UserID'] + transaction_cols
120
+
121
+ # List of all DataFrames to be merged
122
+ self.dataframes = [user_profiles, uplift_data, irrelevant_data, transaction_data]
123
+
124
+ # Merge all DataFrames on 'UserID' in one line
125
+ self.df = reduce(lambda left, right: pd.merge(left, right, on='UserID'), self.dataframes)
126
+
127
+
128
+ def add_monetary_effect(self):
129
+ # Adding a monetary effect column
130
+ def base_price(df, informative_features):
131
+ if df.conversion == 0:
132
+ base_price = 0
133
+ else:
134
+ base_price = randint(1, 100)
135
+ return base_price
136
+
137
+ informative_features = [k for k in self.X_names if 'informative' in k]
138
+ self.df['base_price'] = self.df.apply(lambda x: base_price(x, informative_features), axis=1)
139
+ self.df['discounted_price'] = self.df['base_price']*(1-self.df['discount'])
140
+ self.df['benefit'] = self.df['discounted_price']-0.8*self.df['base_price']
data_utils/data_simulation.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ class UpliftSimulationReady:
4
+
5
+ def __init__(self, files_path):
6
+ self.files_path = files_path
7
+
8
+ def load_user_profiles(self, file_name):
9
+ user_profiles = pd.read_csv(self.files_path + file_name)
10
+ return user_profiles
11
+
12
+ def load_uplift_data(self, file_name):
13
+ uplift_data = pd.read_csv(self.files_path + file_name)
14
+ return uplift_data
15
+
16
+ def load_irrelevant_data(self, file_name):
17
+ irrelevant_data = pd.read_csv(self.files_path + file_name)
18
+ return irrelevant_data
19
+
20
+ def load_other_data(self, file_name):
21
+ transaction_data = pd.read_csv(self.files_path + file_name)
22
+ return transaction_data
data_utils/eda_simulation.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from data_utils.data_simulation import UpliftSimulationReady
4
+
5
+ class EDASimulationReady:
6
+
7
+ def __init__(self, files_path):
8
+ self.files_path = files_path
9
+
10
+ def load_conversions(self, file_name):
11
+
12
+ uplift_simulation = UpliftSimulationReady(self.files_path)
13
+ df = uplift_simulation.load_uplift_data(file_name)
14
+
15
+ sum_conversions = df.pivot_table(values=['conversion','discounted_price','benefit'],
16
+ index='treatment_group_key',
17
+ aggfunc=[np.sum],
18
+ margins=False)
19
+
20
+ mean_conversions = df.pivot_table(values=['conversion','discounted_price','benefit'],
21
+ index='treatment_group_key',
22
+ aggfunc=[np.mean],
23
+ margins=False)
24
+
25
+ # save to csv
26
+ sum_conversions.to_csv(self.files_path + 'sum_conversions.csv')
27
+ mean_conversions.to_csv(self.files_path + 'mean_conversions.csv')
28
+
29
+ return sum_conversions, mean_conversions
data_utils/exploratory_data_analysis.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ class ExploratoryAnalysis:
4
+ def __init__(self, df):
5
+ self.df = df
6
+
7
+ def compute_summaries(self):
8
+ sum_conversions = self.df.pivot_table(values=['conversion', 'discounted_price', 'benefit'],
9
+ index='treatment_group_key',
10
+ aggfunc='sum',
11
+ margins=False)
12
+
13
+ mean_conversions = self.df.pivot_table(values=['conversion', 'discounted_price', 'benefit'],
14
+ index='treatment_group_key',
15
+ aggfunc='mean',
16
+ margins=False)
17
+ return sum_conversions, mean_conversions
18
+
19
+ def compute_mean_benefit_vs_conversion(self):
20
+ _, mean_conversions = self.compute_summaries()
21
+ return mean_conversions[['conversion', 'benefit']]
22
+
23
+ def compute_ate(self):
24
+ _, mean_conversions = self.compute_summaries()
25
+ control_mean = mean_conversions.loc['control']
26
+ mean_conversions_ate = mean_conversions - control_mean
27
+ return mean_conversions_ate
data_utils/feature_importance.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from causalml.feature_selection.filters import FilterSelect
2
+
3
+ class FeatureImportance:
4
+
5
+ def __init__(self, df, X_names, y_name, treatment_group):
6
+ self.df = df
7
+ self.X_names = X_names
8
+ self.y_name = y_name
9
+ self.treatment_group = treatment_group
10
+
11
+ def compute_feature_importance(self):
12
+
13
+ filter_method = FilterSelect()
14
+ method = 'KL'
15
+ kl_imp = filter_method.get_importance(self.df, self.X_names, self.y_name, method,
16
+ treatment_group = self.treatment_group,
17
+ n_bins=20)
18
+ return kl_imp
data_utils/feature_importance_simulation.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from data_utils.data_simulation import UpliftSimulationReady
4
+
5
+ class FISimulationReady:
6
+
7
+ def __init__(self, files_path):
8
+ self.files_path = files_path
9
+
10
+ def load_feature_importance(self, file_name):
11
+ uplift_simulation = UpliftSimulationReady(self.files_path)
12
+ df = uplift_simulation.load_uplift_data(file_name)
13
+ return df
eval_utils/evaluation.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ from causalml.metrics import *
4
+
5
+ class ModelEvaluator:
6
+ def __init__(self, model, df_eval, X_names):
7
+ self.model = model
8
+ self.df_eval = df_eval
9
+ self.X_names = X_names
10
+
11
+ def predict_cate(self, discount):
12
+ """
13
+ Predicts the Conditional Average Treatment Effect (CATE) for a given discount level.
14
+ """
15
+ self.df_eval['cate'] = self.model.predict(
16
+ X=self.df_eval[self.X_names].values,
17
+ treatment=self.df_eval['treatment_group_key'].values
18
+ ).tolist()
19
+ self.df_eval[['cate_discount_05', 'cate_discount_10', 'cate_discount_15']] = pd.DataFrame(
20
+ self.df_eval.cate.tolist(),
21
+ index=self.df_eval.index
22
+ )
23
+
24
+ def eval_performance(self, discount):
25
+ """
26
+ Evaluates the model's performance for a specific discount, calculating Qini curves for conversion and benefit.
27
+ """
28
+ # Ensure CATE predictions are available
29
+ if 'cate' not in self.df_eval.columns:
30
+ self.predict_cate(discount)
31
+
32
+ df_eval_disc = self.df_eval[self.df_eval['treatment_group_key'].isin(['control', discount])]
33
+ df_eval_disc['treatment_num'] = df_eval_disc.apply(
34
+ lambda x: 0 if x['treatment_group_key'] == 'control' else 1,
35
+ axis=1
36
+ )
37
+
38
+ cate_col = 'cate_{}'.format(discount)
39
+
40
+ df_eval_qini_conversion = pd.DataFrame(
41
+ [df_eval_disc[cate_col].ravel(), df_eval_disc.treatment_num.ravel(), df_eval_disc['conversion'].ravel()],
42
+ index=['S', 'w', 'y']
43
+ ).T
44
+
45
+ df_eval_qini_benefit = pd.DataFrame(
46
+ [df_eval_disc[cate_col].ravel(), df_eval_disc.treatment_num.ravel(), df_eval_disc['benefit'].ravel()],
47
+ index=['S', 'w', 'y']
48
+ ).T
49
+
50
+ # Assuming get_qini function exists and calculates Qini coefficient
51
+ cd_conversion = (get_qini(df_eval_qini_conversion) * 2).reset_index()
52
+ cd_conversion = cd_conversion / cd_conversion.shape[0]
53
+
54
+ cd_benefit = (get_qini(df_eval_qini_benefit) * 2).reset_index()
55
+ cd_benefit = cd_benefit / cd_benefit.shape[0]
56
+
57
+ return cd_conversion, cd_benefit
eval_utils/evaluation_simulation.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ class CATEConversionEvaluateSimulationReady:
4
+
5
+ def __init__(self,data_path):
6
+ self.data_path = data_path
7
+
8
+ def evaluate(self, discount_group):
9
+ if discount_group == 5:
10
+ qini_05_conversion_test = pd.read_csv(self.data_path + 'qini_05_conversion_test.csv').drop(columns='Unnamed: 0')
11
+ qini_05_benefit_test = pd.read_csv(self.data_path + 'qini_05_benefit_test.csv').drop(columns='Unnamed: 0')
12
+ return qini_05_conversion_test, qini_05_benefit_test
13
+ elif discount_group == 10:
14
+ qini_10_conversion_test = pd.read_csv(self.data_path + 'qini_10_conversion_test.csv').drop(columns='Unnamed: 0')
15
+ qini_10_benefit_test = pd.read_csv(self.data_path + 'qini_10_benefit_test.csv').drop(columns='Unnamed: 0')
16
+ return qini_10_conversion_test, qini_10_benefit_test
17
+ elif discount_group == 15:
18
+ qini_15_conversion_test = pd.read_csv(self.data_path + 'qini_15_conversion_test.csv').drop(columns='Unnamed: 0')
19
+ qini_15_benefit_test = pd.read_csv(self.data_path + 'qini_15_benefit_test.csv').drop(columns='Unnamed: 0')
20
+ return qini_15_conversion_test, qini_15_benefit_test
21
+
22
+ class CATEBenefitEvaluateSimulationReady:
23
+
24
+ def __init__(self,data_path):
25
+ self.data_path = data_path
26
+
27
+ def evaluate(self, discount_group):
28
+ if discount_group == 5:
29
+ qini_05_conversion_test = pd.read_csv(self.data_path + 'qini_05_conversion_test_bate.csv').drop(columns='Unnamed: 0')
30
+ qini_05_benefit_test = pd.read_csv(self.data_path + 'qini_05_benefit_test_bate.csv').drop(columns='Unnamed: 0')
31
+ return qini_05_conversion_test, qini_05_benefit_test
32
+ elif discount_group == 10:
33
+ qini_10_conversion_test = pd.read_csv(self.data_path + 'qini_10_conversion_test_bate.csv').drop(columns='Unnamed: 0')
34
+ qini_10_benefit_test = pd.read_csv(self.data_path + 'qini_10_benefit_test_bate.csv').drop(columns='Unnamed: 0')
35
+ return qini_10_conversion_test, qini_10_benefit_test
36
+ elif discount_group == 15:
37
+ qini_15_conversion_test = pd.read_csv(self.data_path + 'qini_15_conversion_test_bate.csv').drop(columns='Unnamed: 0')
38
+ qini_15_benefit_test = pd.read_csv(self.data_path + 'qini_15_benefit_test_bate.csv').drop(columns='Unnamed: 0')
39
+ return qini_15_conversion_test, qini_15_benefit_test
mlops_utils/wandb_utils.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import wandb
2
+ import pandas as pd
3
+ import os
4
+
5
+ def upload_dataset_to_wandb(dirs, project_name, dataset_name, dataset_type='raw_dataset'):
6
+ with wandb.init(project=project_name, job_type='load-data') as run:
7
+ dataset_artifact = wandb.Artifact(dataset_name, type=dataset_type)
8
+ for dir in dirs:
9
+ dataset_artifact.add_dir(dir)
10
+ run.log_artifact(dataset_artifact)
11
+
12
+ def eda_work_with_dataset_to_wandb(dirs, project_name, dataset_name, dataset_type, artifact_type):
13
+ with wandb.init(project=project_name, job_type='eda') as run:
14
+ dataset_artifact = run.use_artifact(dataset_name, type=dataset_type)
15
+ eda_artifact = wandb.Artifact('eda_result', type=artifact_type)
16
+ for dir in dirs:
17
+ eda_artifact.add_dir(dir)
18
+ run.log_artifact(eda_artifact)
19
+
20
+ run.log({
21
+ "eda_result": pd.read_csv(
22
+ os.path.join(dirs[0], "kl_feature_importance.csv")
23
+ )
24
+ }
25
+ )
26
+
27
+ def training_results_to_wandb(dirs, project_name, dataset_name, dataset_type, artifact_type, model_name, job_type='train'):
28
+ with wandb.init(project=project_name, job_type=job_type) as run:
29
+ dataset_artifact = run.use_artifact(dataset_name, type=dataset_type)
30
+ model_artifact = wandb.Artifact(model_name, type=artifact_type)
31
+ for dir in dirs:
32
+ model_artifact.add_dir(dir)
33
+ run.log_artifact(model_artifact)
34
+
35
+ if job_type == 'train':
36
+ run.log({
37
+ "discount_05_feature_importance": pd.read_csv(
38
+ os.path.join(dirs[0], "discount_05_feature_importance.csv")
39
+ ),
40
+ "discount_10_feature_importance": pd.read_csv(
41
+ os.path.join(dirs[0], "discount_10_feature_importance.csv")
42
+ ),
43
+ "discount_15_feature_importance": pd.read_csv(
44
+ os.path.join(dirs[0], "discount_15_feature_importance.csv")
45
+ ),
46
+ }
47
+ )
models_utils/ml_models.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sklearn.model_selection import train_test_split
2
+ from xgboost import XGBRegressor, XGBClassifier
3
+
4
+ from causalml.inference.tree import UpliftRandomForestClassifier
5
+ from causalml.inference.meta import BaseXRegressor, BaseRRegressor, BaseSRegressor, BaseTRegressor
6
+ from causalml.inference.meta import BaseSClassifier, BaseTClassifier, BaseXClassifier, BaseRClassifier
7
+
8
+ class ModelTraining:
9
+ def __init__(self, df, y_name, X_names):
10
+ self.df = df
11
+ self.y_name = y_name
12
+ self.X_names = X_names
13
+ self.df_train = None
14
+ self.df_test = None
15
+ self.learner_t = None
16
+ self.conversion_learner_t = None
17
+ self.benefit_learner_t = None
18
+
19
+ def split_data(self, test_size, random_state):
20
+ self.df_train, self.df_test = train_test_split(
21
+ self.df,
22
+ test_size=test_size,
23
+ random_state=random_state
24
+ )
25
+
26
+ def fit_predict_classifier(self, params, control_name):
27
+ self.learner_t = BaseTClassifier(XGBClassifier(**params), control_name=control_name)
28
+ self.conversion_learner_t = self.learner_t
29
+ return self._fit_predict()
30
+
31
+ def fit_predict_regressor(self, params, control_name):
32
+ self.learner_t = BaseTRegressor(XGBRegressor(**params), control_name=control_name)
33
+ self.benefit_learner_t = self.learner_t
34
+ return self._fit_predict()
35
+
36
+ def _fit_predict(self):
37
+ self.learner_t_tau = self.learner_t.fit_predict(
38
+ X=self.df_train[self.X_names].values,
39
+ treatment=self.df_train['treatment_group_key'].values,
40
+ y=self.df_train[self.y_name].values
41
+ )
42
+ self.learner_t.feature_names = self.X_names
43
+ return self.learner_t_tau
44
+
45
+ def compute_feature_importance(self):
46
+ if self.learner_t is None:
47
+ raise ValueError("Model must be fitted before computing feature importances.")
48
+
49
+ return self.learner_t.get_importance(
50
+ X=self.df_train[self.X_names],
51
+ tau=self.learner_t_tau,
52
+ features=self.X_names,
53
+ normalize=True,
54
+ method='auto'
55
+ )
models_utils/models_simulation.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import pickle
3
+
4
+ class CATESimulationReady:
5
+
6
+ def __init__(self, model_path, y_pred_path):
7
+ self.model_path = model_path
8
+ self.y_pred_path = y_pred_path
9
+
10
+ # def get_model(self):
11
+ # model = pd.read_csv(self.model_path)
12
+ # return model
13
+
14
+ def predict(self):
15
+ with open(self.y_pred_path, 'rb') as f:
16
+ y_pred = pickle.load(f)
17
+ return y_pred
18
+
19
+ def feature_importance(self, fi_path):
20
+ fi = pd.read_csv(fi_path)
21
+ fi.columns = ['feature', 'score']
22
+ return fi
notebooks/Demo_Notebook.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
notebooks/Test.ipynb ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 11,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import pandas as pd\n",
10
+ "import matplotlib.pyplot as plt\n",
11
+ "\n",
12
+ "class CATEEvaluateSimulationReady:\n",
13
+ "\n",
14
+ " def __init__(self,data_path):\n",
15
+ " self.data_path = data_path\n",
16
+ "\n",
17
+ " def evaluate(self, discount_group):\n",
18
+ " if discount_group == 5:\n",
19
+ " qini_05_conversion_test = pd.read_csv(self.data_path + 'qini_05_conversion_test.csv').drop(columns='Unnamed: 0')\n",
20
+ " qini_05_benefit_test = pd.read_csv(self.data_path + 'qini_05_benefit_test.csv').drop(columns='Unnamed: 0')\n",
21
+ " return qini_05_conversion_test, qini_05_benefit_test\n",
22
+ " elif discount_group == 10:\n",
23
+ " qini_10_conversion_test = pd.read_csv(self.data_path + 'qini_10_conversion_test.csv').drop(columns='Unnamed: 0')\n",
24
+ " qini_10_benefit_test = pd.read_csv(self.data_path + 'qini_10_benefit_test.csv').drop(columns='Unnamed: 0')\n",
25
+ " return qini_10_conversion_test, qini_10_benefit_test\n",
26
+ " elif discount_group == 15:\n",
27
+ " qini_15_conversion_test = pd.read_csv(self.data_path + 'qini_15_conversion_test.csv').drop(columns='Unnamed: 0')\n",
28
+ " qini_15_benefit_test = pd.read_csv(self.data_path + 'qini_15_benefit_test.csv').drop(columns='Unnamed: 0')\n",
29
+ " return qini_15_conversion_test, qini_15_benefit_test"
30
+ ]
31
+ },
32
+ {
33
+ "cell_type": "code",
34
+ "execution_count": 7,
35
+ "metadata": {},
36
+ "outputs": [],
37
+ "source": [
38
+ "eval = CATEEvaluateSimulationReady('../data/effect_data/')\n",
39
+ "qini_05_conversion_test, qini_05_benefit_test = eval.evaluate(5)\n",
40
+ "qini_10_conversion_test, qini_10_benefit_test = eval.evaluate(10)\n",
41
+ "qini_15_conversion_test, qini_15_benefit_test = eval.evaluate(15)"
42
+ ]
43
+ },
44
+ {
45
+ "cell_type": "code",
46
+ "execution_count": 23,
47
+ "metadata": {},
48
+ "outputs": [
49
+ {
50
+ "data": {
51
+ "text/plain": [
52
+ "Text(0.5, 1.0, 'CATE conversion vs Targeted Population')"
53
+ ]
54
+ },
55
+ "execution_count": 23,
56
+ "metadata": {},
57
+ "output_type": "execute_result"
58
+ },
59
+ {
60
+ "data": {
61
+ "image/png": "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",
62
+ "text/plain": [
63
+ "<Figure size 432x288 with 1 Axes>"
64
+ ]
65
+ },
66
+ "metadata": {
67
+ "needs_background": "light"
68
+ },
69
+ "output_type": "display_data"
70
+ }
71
+ ],
72
+ "source": [
73
+ "fig_conversion, ax_conversion = plt.subplots()\n",
74
+ "\n",
75
+ "qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')\n",
76
+ "qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')\n",
77
+ "qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')\n",
78
+ "qini_05_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='b', label = '5% model')\n",
79
+ "qini_10_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='g', label = '10% model')\n",
80
+ "qini_15_conversion_test.plot(ax=ax_conversion, x='index', y='S', color='y', label = '15% model')\n",
81
+ "\n",
82
+ "ax_conversion.legend()\n",
83
+ "ax_conversion.set_xlabel('Fraction of Targeted Users')\n",
84
+ "ax_conversion.set_ylabel('CATE conversion')\n",
85
+ "ax_conversion.set_title('CATE conversion vs Targeted Population')"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": 26,
91
+ "metadata": {},
92
+ "outputs": [
93
+ {
94
+ "data": {
95
+ "text/plain": [
96
+ "Text(0.5, 1.0, 'CATE benefit vs Targeted Population')"
97
+ ]
98
+ },
99
+ "execution_count": 26,
100
+ "metadata": {},
101
+ "output_type": "execute_result"
102
+ },
103
+ {
104
+ "data": {
105
+ "image/png": "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",
106
+ "text/plain": [
107
+ "<Figure size 432x288 with 1 Axes>"
108
+ ]
109
+ },
110
+ "metadata": {
111
+ "needs_background": "light"
112
+ },
113
+ "output_type": "display_data"
114
+ }
115
+ ],
116
+ "source": [
117
+ "fig_benefit, ax_benefit = plt.subplots()\n",
118
+ "qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='b', ls='--', lw=0.5, label = '5% random')\n",
119
+ "qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='g', ls='--', lw=0.5, label = '10% random')\n",
120
+ "qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='Random', color='y', ls='--', lw=0.5, label = '15% random')\n",
121
+ "qini_05_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='b', label = '5% model')\n",
122
+ "qini_10_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='g', label = '10% model')\n",
123
+ "qini_15_benefit_test.plot(ax=ax_benefit, x='index', y='S', color='y', label = '15% model')\n",
124
+ "ax_benefit.legend()\n",
125
+ "ax_benefit.set_xlabel('Fraction of Targeted Users')\n",
126
+ "ax_benefit.set_ylabel('CATE Benefit')\n",
127
+ "ax_benefit.set_title('CATE benefit vs Targeted Population')"
128
+ ]
129
+ }
130
+ ],
131
+ "metadata": {
132
+ "kernelspec": {
133
+ "display_name": "Python 3",
134
+ "language": "python",
135
+ "name": "python3"
136
+ },
137
+ "language_info": {
138
+ "codemirror_mode": {
139
+ "name": "ipython",
140
+ "version": 3
141
+ },
142
+ "file_extension": ".py",
143
+ "mimetype": "text/x-python",
144
+ "name": "python",
145
+ "nbconvert_exporter": "python",
146
+ "pygments_lexer": "ipython3",
147
+ "version": "3.9.6"
148
+ }
149
+ },
150
+ "nbformat": 4,
151
+ "nbformat_minor": 2
152
+ }
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ causalml==0.15.0
2
+ matplotlib==3.8.3
3
+ numpy==1.23.5
4
+ pandas==2.2.1
5
+ scikit_learn==1.4.1.post1
6
+ streamlit==1.32.2
7
+ xgboost==2.0.3