HardWorkingStation commited on
Commit
44e7893
1 Parent(s): 3a9a180

Initial commit

Browse files
Files changed (3) hide show
  1. src/test.ipynb +45 -112
  2. src/tools.py +95 -1
  3. src/web_app.py +79 -21
src/test.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 9,
6
  "metadata": {
7
  "collapsed": true,
8
  "pycharm": {
@@ -25,59 +25,20 @@
25
  {
26
  "cell_type": "code",
27
  "execution_count": 2,
28
- "outputs": [],
29
- "source": [
30
- "def get_data() -> tuple[Any, Any, Any]:\n",
31
- "\t# получаем датасет\n",
32
- "\tdataset = fetch_hillstrom(target_col='visit')\n",
33
- "\tdataset, target, treatment = dataset['data'], dataset['target'], dataset['treatment']\n",
34
- "\t# выбираем два сегмента\n",
35
- "\tdataset = dataset[treatment != 'Mens E-Mail']\n",
36
- "\ttarget = target[treatment != 'Mens E-Mail']\n",
37
- "\ttreatment = treatment[treatment != 'Mens E-Mail'].map({\n",
38
- "\t\t'Womens E-Mail': 1,\n",
39
- "\t\t'No E-Mail': 0\n",
40
- "\t})\n",
41
- "\n",
42
- "\treturn dataset, target, treatment"
43
- ],
44
- "metadata": {
45
- "collapsed": false,
46
- "pycharm": {
47
- "name": "#%%\n"
48
- }
49
- }
50
- },
51
- {
52
- "cell_type": "code",
53
- "execution_count": 3,
54
- "outputs": [],
55
- "source": [
56
- "data, target, treatment = get_data()"
57
- ],
58
- "metadata": {
59
- "collapsed": false,
60
- "pycharm": {
61
- "name": "#%%\n"
62
- }
63
- }
64
- },
65
- {
66
- "cell_type": "code",
67
- "execution_count": 4,
68
  "outputs": [
69
  {
70
  "data": {
71
- "text/plain": " recency history_segment history mens womens zip_code newbie channel\n0 10 2) $100 - $200 142.44 1 0 Surburban 0 Phone\n1 6 3) $200 - $350 329.08 1 1 Rural 1 Web\n2 7 2) $100 - $200 180.65 0 1 Surburban 1 Web\n4 2 1) $0 - $100 45.34 1 0 Urban 0 Web\n5 6 2) $100 - $200 134.83 0 1 Surburban 0 Phone",
72
- "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>recency</th>\n <th>history_segment</th>\n <th>history</th>\n <th>mens</th>\n <th>womens</th>\n <th>zip_code</th>\n <th>newbie</th>\n <th>channel</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>10</td>\n <td>2) $100 - $200</td>\n <td>142.44</td>\n <td>1</td>\n <td>0</td>\n <td>Surburban</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>1</th>\n <td>6</td>\n <td>3) $200 - $350</td>\n <td>329.08</td>\n <td>1</td>\n <td>1</td>\n <td>Rural</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>2</th>\n <td>7</td>\n <td>2) $100 - $200</td>\n <td>180.65</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>4</th>\n <td>2</td>\n <td>1) $0 - $100</td>\n <td>45.34</td>\n <td>1</td>\n <td>0</td>\n <td>Urban</td>\n <td>0</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>5</th>\n <td>6</td>\n <td>2) $100 - $200</td>\n <td>134.83</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n </tbody>\n</table>\n</div>"
73
  },
74
- "execution_count": 4,
75
  "metadata": {},
76
  "output_type": "execute_result"
77
  }
78
  ],
79
  "source": [
80
- "data.head()"
 
 
81
  ],
82
  "metadata": {
83
  "collapsed": false,
@@ -88,19 +49,22 @@
88
  },
89
  {
90
  "cell_type": "code",
91
- "execution_count": 5,
92
- "outputs": [
93
- {
94
- "data": {
95
- "text/plain": "0 0\n1 0\n2 0\n4 0\n5 1\nName: visit, dtype: int64"
96
- },
97
- "execution_count": 5,
98
- "metadata": {},
99
- "output_type": "execute_result"
100
- }
101
- ],
102
  "source": [
103
- "target.head()"
 
 
 
 
 
 
 
 
 
 
 
 
104
  ],
105
  "metadata": {
106
  "collapsed": false,
@@ -111,19 +75,10 @@
111
  },
112
  {
113
  "cell_type": "code",
114
- "execution_count": 6,
115
- "outputs": [
116
- {
117
- "data": {
118
- "text/plain": "0 1\n1 0\n2 1\n4 1\n5 1\nName: segment, dtype: int64"
119
- },
120
- "execution_count": 6,
121
- "metadata": {},
122
- "output_type": "execute_result"
123
- }
124
- ],
125
  "source": [
126
- "treatment.head()"
127
  ],
128
  "metadata": {
129
  "collapsed": false,
@@ -134,20 +89,17 @@
134
  },
135
  {
136
  "cell_type": "code",
137
- "execution_count": 23,
138
- "outputs": [
139
- {
140
- "data": {
141
- "text/plain": "visit 0 1\nsegment \n0 0.893833 0.106167\n1 0.848600 0.151400",
142
- "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th>visit</th>\n <th>0</th>\n <th>1</th>\n </tr>\n <tr>\n <th>segment</th>\n <th></th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>0.893833</td>\n <td>0.106167</td>\n </tr>\n <tr>\n <th>1</th>\n <td>0.848600</td>\n <td>0.151400</td>\n </tr>\n </tbody>\n</table>\n</div>"
143
- },
144
- "execution_count": 23,
145
- "metadata": {},
146
- "output_type": "execute_result"
147
- }
148
- ],
149
  "source": [
150
- "pd.crosstab(treatment, target, normalize='index')"
 
 
 
 
 
 
 
151
  ],
152
  "metadata": {
153
  "collapsed": false,
@@ -158,19 +110,20 @@
158
  },
159
  {
160
  "cell_type": "code",
161
- "execution_count": 50,
162
  "outputs": [
163
  {
164
  "data": {
165
- "text/plain": "1 50.261167\n0 49.738833\nName: newbie, dtype: float64"
 
166
  },
 
167
  "metadata": {},
168
- "output_type": "display_data"
169
  }
170
  ],
171
  "source": [
172
- "res = data.newbie.value_counts(normalize=True) * 100\n",
173
- "display(res)"
174
  ],
175
  "metadata": {
176
  "collapsed": false,
@@ -181,40 +134,20 @@
181
  },
182
  {
183
  "cell_type": "code",
184
- "execution_count": 54,
185
  "outputs": [
186
  {
187
  "data": {
188
- "text/plain": "Surburban 19275\nUrban 17098\nRural 6320\nName: zip_code, dtype: int64"
 
189
  },
190
- "execution_count": 54,
191
  "metadata": {},
192
  "output_type": "execute_result"
193
  }
194
  ],
195
  "source": [
196
- "data.zip_code.value_counts()"
197
- ],
198
- "metadata": {
199
- "collapsed": false,
200
- "pycharm": {
201
- "name": "#%%\n"
202
- }
203
- }
204
- },
205
- {
206
- "cell_type": "code",
207
- "execution_count": 12,
208
- "outputs": [],
209
- "source": [
210
- "X_train, X_val, y_train, y_val, trmnt_train, trmnt_val = train_test_split(\n",
211
- " data, target, treatment, test_size=0.3, random_state=42\n",
212
- ")\n",
213
- "\n",
214
- "models_results = {\n",
215
- " 'approach': [],\n",
216
- " 'uplift@30%': []\n",
217
- "}"
218
  ],
219
  "metadata": {
220
  "collapsed": false,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 1,
6
  "metadata": {
7
  "collapsed": true,
8
  "pycharm": {
 
25
  {
26
  "cell_type": "code",
27
  "execution_count": 2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  "outputs": [
29
  {
30
  "data": {
31
+ "text/plain": "{'new_filter': 'filter'}"
 
32
  },
33
+ "execution_count": 2,
34
  "metadata": {},
35
  "output_type": "execute_result"
36
  }
37
  ],
38
  "source": [
39
+ "filters = {}\n",
40
+ "filters['new_filter'] = 'filter'\n",
41
+ "filters"
42
  ],
43
  "metadata": {
44
  "collapsed": false,
 
49
  },
50
  {
51
  "cell_type": "code",
52
+ "execution_count": 4,
53
+ "outputs": [],
 
 
 
 
 
 
 
 
 
54
  "source": [
55
+ "def get_data() -> tuple[Any, Any, Any]:\n",
56
+ "\t# получаем датасет\n",
57
+ "\tdataset = fetch_hillstrom(target_col='visit')\n",
58
+ "\tdataset, target, treatment = dataset['data'], dataset['target'], dataset['treatment']\n",
59
+ "\t# выбираем два сегмента\n",
60
+ "\tdataset = dataset[treatment != 'Mens E-Mail']\n",
61
+ "\ttarget = target[treatment != 'Mens E-Mail']\n",
62
+ "\ttreatment = treatment[treatment != 'Mens E-Mail'].map({\n",
63
+ "\t\t'Womens E-Mail': 1,\n",
64
+ "\t\t'No E-Mail': 0\n",
65
+ "\t})\n",
66
+ "\n",
67
+ "\treturn dataset, target, treatment"
68
  ],
69
  "metadata": {
70
  "collapsed": false,
 
75
  },
76
  {
77
  "cell_type": "code",
78
+ "execution_count": 111,
79
+ "outputs": [],
 
 
 
 
 
 
 
 
 
80
  "source": [
81
+ "data, target, treatment = get_data()"
82
  ],
83
  "metadata": {
84
  "collapsed": false,
 
89
  },
90
  {
91
  "cell_type": "code",
92
+ "execution_count": 112,
93
+ "outputs": [],
 
 
 
 
 
 
 
 
 
 
94
  "source": [
95
+ "X_train, X_val, y_train, y_val, trmnt_train, trmnt_val = train_test_split(\n",
96
+ " data, target, treatment, test_size=0.3, random_state=42\n",
97
+ ")\n",
98
+ "\n",
99
+ "models_results = {\n",
100
+ " 'approach': [],\n",
101
+ " 'uplift@30%': []\n",
102
+ "}"
103
  ],
104
  "metadata": {
105
  "collapsed": false,
 
110
  },
111
  {
112
  "cell_type": "code",
113
+ "execution_count": 113,
114
  "outputs": [
115
  {
116
  "data": {
117
+ "text/plain": " recency history_segment history mens womens zip_code newbie \\\n609 9 3) $200 - $350 212.32 1 0 Surburban 0 \n51952 3 6) $750 - $1,000 849.16 1 1 Surburban 1 \n33629 9 1) $0 - $100 43.22 1 0 Surburban 1 \n22103 8 1) $0 - $100 29.99 0 1 Surburban 0 \n21350 7 2) $100 - $200 164.07 1 0 Surburban 1 \n... ... ... ... ... ... ... ... \n9307 1 2) $100 - $200 110.45 0 1 Urban 0 \n16819 1 1) $0 - $100 88.04 0 1 Surburban 0 \n57173 1 1) $0 - $100 72.63 0 1 Rural 0 \n1282 3 4) $350 - $500 366.16 0 1 Rural 0 \n23624 9 1) $0 - $100 36.87 1 0 Urban 1 \n\n channel \n609 Multichannel \n51952 Multichannel \n33629 Phone \n22103 Web \n21350 Phone \n... ... \n9307 Phone \n16819 Phone \n57173 Phone \n1282 Phone \n23624 Web \n\n[29885 rows x 8 columns]",
118
+ "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>recency</th>\n <th>history_segment</th>\n <th>history</th>\n <th>mens</th>\n <th>womens</th>\n <th>zip_code</th>\n <th>newbie</th>\n <th>channel</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>609</th>\n <td>9</td>\n <td>3) $200 - $350</td>\n <td>212.32</td>\n <td>1</td>\n <td>0</td>\n <td>Surburban</td>\n <td>0</td>\n <td>Multichannel</td>\n </tr>\n <tr>\n <th>51952</th>\n <td>3</td>\n <td>6) $750 - $1,000</td>\n <td>849.16</td>\n <td>1</td>\n <td>1</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Multichannel</td>\n </tr>\n <tr>\n <th>33629</th>\n <td>9</td>\n <td>1) $0 - $100</td>\n <td>43.22</td>\n <td>1</td>\n <td>0</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>22103</th>\n <td>8</td>\n <td>1) $0 - $100</td>\n <td>29.99</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>0</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>21350</th>\n <td>7</td>\n <td>2) $100 - $200</td>\n <td>164.07</td>\n <td>1</td>\n <td>0</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>...</th>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <th>9307</th>\n <td>1</td>\n <td>2) $100 - $200</td>\n <td>110.45</td>\n <td>0</td>\n <td>1</td>\n <td>Urban</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>16819</th>\n <td>1</td>\n <td>1) $0 - $100</td>\n <td>88.04</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>57173</th>\n <td>1</td>\n <td>1) $0 - $100</td>\n <td>72.63</td>\n <td>0</td>\n <td>1</td>\n <td>Rural</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>1282</th>\n <td>3</td>\n <td>4) $350 - $500</td>\n <td>366.16</td>\n <td>0</td>\n <td>1</td>\n <td>Rural</td>\n <td>0</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>23624</th>\n <td>9</td>\n <td>1) $0 - $100</td>\n <td>36.87</td>\n <td>1</td>\n <td>0</td>\n <td>Urban</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n </tbody>\n</table>\n<p>29885 rows × 8 columns</p>\n</div>"
119
  },
120
+ "execution_count": 113,
121
  "metadata": {},
122
+ "output_type": "execute_result"
123
  }
124
  ],
125
  "source": [
126
+ "X_train"
 
127
  ],
128
  "metadata": {
129
  "collapsed": false,
 
134
  },
135
  {
136
  "cell_type": "code",
137
+ "execution_count": 114,
138
  "outputs": [
139
  {
140
  "data": {
141
+ "text/plain": " recency history_segment history mens womens zip_code newbie \\\n7730 3 5) $500 - $750 503.73 0 1 Rural 1 \n17594 5 2) $100 - $200 163.39 1 0 Urban 0 \n14481 11 3) $200 - $350 287.77 1 0 Rural 1 \n20003 3 1) $0 - $100 29.99 0 1 Surburban 1 \n19981 5 2) $100 - $200 131.51 0 1 Urban 1 \n... ... ... ... ... ... ... ... \n41828 11 4) $350 - $500 457.09 0 1 Surburban 1 \n49354 2 1) $0 - $100 29.99 0 1 Rural 1 \n12063 9 2) $100 - $200 147.48 1 0 Rural 1 \n3757 10 2) $100 - $200 128.78 1 0 Surburban 1 \n10708 6 2) $100 - $200 138.72 1 0 Urban 0 \n\n channel \n7730 Multichannel \n17594 Web \n14481 Web \n20003 Web \n19981 Web \n... ... \n41828 Multichannel \n49354 Phone \n12063 Phone \n3757 Phone \n10708 Web \n\n[12808 rows x 8 columns]",
142
+ "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>recency</th>\n <th>history_segment</th>\n <th>history</th>\n <th>mens</th>\n <th>womens</th>\n <th>zip_code</th>\n <th>newbie</th>\n <th>channel</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>7730</th>\n <td>3</td>\n <td>5) $500 - $750</td>\n <td>503.73</td>\n <td>0</td>\n <td>1</td>\n <td>Rural</td>\n <td>1</td>\n <td>Multichannel</td>\n </tr>\n <tr>\n <th>17594</th>\n <td>5</td>\n <td>2) $100 - $200</td>\n <td>163.39</td>\n <td>1</td>\n <td>0</td>\n <td>Urban</td>\n <td>0</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>14481</th>\n <td>11</td>\n <td>3) $200 - $350</td>\n <td>287.77</td>\n <td>1</td>\n <td>0</td>\n <td>Rural</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>20003</th>\n <td>3</td>\n <td>1) $0 - $100</td>\n <td>29.99</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>19981</th>\n <td>5</td>\n <td>2) $100 - $200</td>\n <td>131.51</td>\n <td>0</td>\n <td>1</td>\n <td>Urban</td>\n <td>1</td>\n <td>Web</td>\n </tr>\n <tr>\n <th>...</th>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <th>41828</th>\n <td>11</td>\n <td>4) $350 - $500</td>\n <td>457.09</td>\n <td>0</td>\n <td>1</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Multichannel</td>\n </tr>\n <tr>\n <th>49354</th>\n <td>2</td>\n <td>1) $0 - $100</td>\n <td>29.99</td>\n <td>0</td>\n <td>1</td>\n <td>Rural</td>\n <td>1</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>12063</th>\n <td>9</td>\n <td>2) $100 - $200</td>\n <td>147.48</td>\n <td>1</td>\n <td>0</td>\n <td>Rural</td>\n <td>1</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>3757</th>\n <td>10</td>\n <td>2) $100 - $200</td>\n <td>128.78</td>\n <td>1</td>\n <td>0</td>\n <td>Surburban</td>\n <td>1</td>\n <td>Phone</td>\n </tr>\n <tr>\n <th>10708</th>\n <td>6</td>\n <td>2) $100 - $200</td>\n <td>138.72</td>\n <td>1</td>\n <td>0</td>\n <td>Urban</td>\n <td>0</td>\n <td>Web</td>\n </tr>\n </tbody>\n</table>\n<p>12808 rows × 8 columns</p>\n</div>"
143
  },
144
+ "execution_count": 114,
145
  "metadata": {},
146
  "output_type": "execute_result"
147
  }
148
  ],
149
  "source": [
150
+ "X_val"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  ],
152
  "metadata": {
153
  "collapsed": false,
src/tools.py CHANGED
@@ -4,6 +4,7 @@ import pandas as pd
4
  import numpy as np
5
  from sklearn.model_selection import train_test_split
6
  from sklift.datasets import fetch_hillstrom
 
7
  from catboost import CatBoostClassifier
8
  import sklearn
9
  import streamlit as st
@@ -28,7 +29,7 @@ def get_data() -> tuple[Any, Any, Any]:
28
 
29
 
30
  @st.experimental_memo
31
- def data_split(data, treatment, target) -> tuple[Any, Any, Any, Any, Any, Any]:
32
  # склеиваем threatment и target для дальнейшей стратификации по ним
33
  stratify_cols = pd.concat([treatment, target], axis=1)
34
  # сплитим датасет
@@ -43,6 +44,99 @@ def data_split(data, treatment, target) -> tuple[Any, Any, Any, Any, Any, Any]:
43
  return X_train, X_val, trmnt_train, trmnt_val, y_train, y_val
44
 
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def get_newbie_plot(data):
47
  fig = px.histogram(
48
  data['newbie'],
 
4
  import numpy as np
5
  from sklearn.model_selection import train_test_split
6
  from sklift.datasets import fetch_hillstrom
7
+ from sklift.metrics import uplift_at_k
8
  from catboost import CatBoostClassifier
9
  import sklearn
10
  import streamlit as st
 
29
 
30
 
31
  @st.experimental_memo
32
+ def data_split(data: pd.DataFrame, treatment: pd.DataFrame, target: pd.DataFrame) -> tuple[Any, Any, Any, Any, Any, Any]:
33
  # склеиваем threatment и target для дальнейшей стратификации по ним
34
  stratify_cols = pd.concat([treatment, target], axis=1)
35
  # сплитим датасет
 
44
  return X_train, X_val, trmnt_train, trmnt_val, y_train, y_val
45
 
46
 
47
+ def filter_by_newbie(data: pd.DataFrame, newbie_filter: str) -> pd.DataFrame:
48
+ if newbie_filter == 'Всем':
49
+ return data
50
+ elif newbie_filter == 'Только новым':
51
+ return data[data['newbie'] == 1]
52
+ elif newbie_filter == 'Только старым':
53
+ return data[data['newbie'] == 0]
54
+
55
+
56
+ def filter_by_channel(data: pd.DataFrame, channel_filter: str) -> pd.DataFrame:
57
+ if channel_filter == 'Всем':
58
+ return data
59
+ if channel_filter == 'Phone':
60
+ return data[data['channel'] == channel_filter]
61
+ if channel_filter == 'Web':
62
+ return data[data['channel'] == channel_filter]
63
+ if channel_filter == 'Multichannel':
64
+ return data[data['channel'] == channel_filter]
65
+
66
+
67
+ def filter_by_mens(data: pd.DataFrame, mens_filter: str) -> pd.DataFrame:
68
+ if mens_filter == 'Любые товары':
69
+ return data
70
+ if mens_filter == 'Мужские':
71
+ return data[data['mens'] == 1]
72
+ if mens_filter == 'Женские':
73
+ return data[data['womens'] == 1]
74
+
75
+
76
+ def filter_by_history_segments(data: pd.DataFrame, history_segments_filter: dict) -> pd.DataFrame:
77
+ filtered_indexes = set()
78
+ if history_segments_filter.get('1) $0 - $100'):
79
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '1) $0 - $100'].index)
80
+ if history_segments_filter.get('2) $100 - $200'):
81
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '2) $100 - $200'].index)
82
+ if history_segments_filter.get('3) $200 - $350'):
83
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '3) $200 - $350'].index)
84
+ if history_segments_filter.get('4) $350 - $500'):
85
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '4) $350 - $500'].index)
86
+ if history_segments_filter.get('5) $500 - $750'):
87
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '5) $500 - $750'].index)
88
+ if history_segments_filter.get('6) $750 - $1,000'):
89
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '6) $750 - $1,000'].index)
90
+ if history_segments_filter.get('7) $1,000 +'):
91
+ filtered_indexes = filtered_indexes.union(data[data['history_segment'] == '7) $1,000 +'].index)
92
+
93
+ return data.loc[list(filtered_indexes)]
94
+
95
+
96
+ def filter_by_zip_code(data: pd.DataFrame, zip_code_filter: dict) -> pd.DataFrame:
97
+ filterd_indexes = set()
98
+ if zip_code_filter.get('surburban'):
99
+ filterd_indexes = filterd_indexes.union(data[data['zip_code'] == 'Surburban'].index)
100
+ if zip_code_filter.get('urban'):
101
+ filterd_indexes = filterd_indexes.union(data[data['zip_code'] == 'Urban'].index)
102
+ if zip_code_filter.get('rural'):
103
+ filterd_indexes = filterd_indexes.union(data[data['zip_code'] == 'Rural'].index)
104
+
105
+ return data.loc[list(filterd_indexes)]
106
+
107
+
108
+ def filter_by_recency(data: pd.DataFrame, recency_filter: list) -> pd.DataFrame:
109
+ return data[(data['recency'] >= recency_filter[0]) & (data['recency'] <= recency_filter[1])]
110
+
111
+
112
+ def filter_data(data: pd.DataFrame, filters: dict) -> pd.DataFrame or None:
113
+ data = filter_by_newbie(data, filters['newbie_filter'])
114
+ if data.shape[0] == 0:
115
+ return None
116
+ data = filter_by_channel(data, filters['channel_filter'])
117
+ if data.shape[0] == 0:
118
+ return None
119
+ data = filter_by_mens(data, filters['mens_filter'])
120
+ if data.shape[0] == 0:
121
+ return None
122
+ data = filter_by_history_segments(data, filters['history_segments'])
123
+ if data.shape[0] == 0:
124
+ return None
125
+ data = filter_by_zip_code(data, filters['zip_code'])
126
+ if data.shape[0] == 0:
127
+ return None
128
+ data = filter_by_recency(data, filters['recency'])
129
+ if data.shape[0] == 0:
130
+ return None
131
+ return data
132
+
133
+
134
+ def send_promo_and_get_res(data_train: pd.DataFrame, treatment: pd.DataFrame, target: pd.DataFrame):
135
+ indexes = data.index
136
+ target = target.loc[indexes]
137
+ treatment = treatment.loc[indexes]
138
+
139
+
140
  def get_newbie_plot(data):
141
  fig = px.histogram(
142
  data['newbie'],
src/web_app.py CHANGED
@@ -7,6 +7,8 @@ import tools
7
 
8
  dataset, target, treatment = tools.get_data()
9
 
 
 
10
  st.title('Uplift lab')
11
 
12
  st.markdown(
@@ -17,6 +19,8 @@ st.markdown(
17
 
18
  Этот набор данных содержит 42 693 строк с данными клиентов, которые в последний раз совершали покупки в течение двенадцати месяцев.
19
 
 
 
20
  Среди клиентов была проведена рекламная кампания с помощью _email_ рассылки:
21
  - 1/2 клиентов были выбраны случайным образом для получения электронного письма, рекламирующего женскую продукцию;
22
  - С оставшейся 1/2 коммуникацию не проводили.
@@ -28,11 +32,11 @@ st.markdown(
28
  """
29
  )
30
  refresh = st.button('Обновить выборку')
31
- title_subsample = dataset.sample(7)
32
  if refresh:
33
- title_subsample = dataset.sample(7)
34
  st.dataframe(title_subsample, width=700)
35
- st.write( f"Всего записей: {dataset.shape[0]}")
36
 
37
  st.write('Описание данных')
38
  st.markdown(
@@ -56,59 +60,113 @@ st.write("Для того, чтобы лучше понять на какую а
56
 
57
  with st.expander('Развернуть блок анализа данных'):
58
 
59
- st.plotly_chart(tools.get_newbie_plot(dataset), use_container_width=True)
60
  st.write(f'В данных примерно одинаковое количество новых и "старых клиентов". '
61
- f'Отношение новых клиентов к старым: {(dataset["newbie"] == 1).sum() / (dataset["newbie"] == 0).sum():.2f}')
62
 
63
- st.plotly_chart(tools.get_zipcode_plot(dataset), use_container_width=True)
64
- tmp_res = dataset.zip_code.value_counts(normalize=True) * 100
65
  st.write(f'Большинство клиентов из пригорода: {tmp_res["Surburban"]:.2f}%, из города: {tmp_res["Urban"]:.2f}% и из села: {tmp_res["Rural"]:.2f}%')
66
 
67
- tmp_res = dataset.channel.value_counts(normalize=True) * 100
68
- st.plotly_chart(tools.get_channel_plot(dataset), use_container_width=True)
69
  st.write(f'В прошлом году почти одинаковое количество клиентов покупало товары через телефон и сайт, {tmp_res["Phone"]:.2f}% и {tmp_res["Web"]:.2f}% соответственно,'
70
  f' а {tmp_res["Multichannel"]:.2f}% клиентов покупали товары воспользовавшись двумя платформами.')
71
 
72
- tmp_res = dataset.history_segment.value_counts(normalize=True) * 100
73
- st.plotly_chart(tools.get_history_segment_plot(dataset), use_container_width=True)
74
  st.write(f'Как мы видим, большинство пользователей относится к сегменту \$0-\$100 ({tmp_res[0]:.2f}%), второй и '
75
  f'третий по количеству пользователей сегменты \$100-\$200 ({tmp_res[1]:.2f}%) и \$200-\$350 ({tmp_res[2]:.2f}%).')
76
  st.write(f'К сегментам \$350-\$500 и \$500-\$750 относится {tmp_res[3]:.2f}% и {tmp_res[4]:.2f}% пользователей соответственно.')
77
  st.write(f'Меньше всего пользователей в сегментах \$750-\$1.000 ({tmp_res[-2]:.2f}%) и \$1.000+ ({tmp_res[-1]:.2f}%).')
78
 
79
- tmp_res = list(dataset.recency.value_counts(normalize=True) * 100)
80
- st.plotly_chart(tools.get_recency_plot(dataset), use_container_width=True)
81
  st.write(f'Большинство клиентов являются активными клиентами платформы, и совершали покупки в течен��е месяца ({tmp_res[0]:.2f}%)')
82
  st.write('Также заметно, что 9 и 10 месяцев назад, много клиентов совершали покупки. Это может свидетельствовать о проведении'
83
  'рекламной кампании в это время или чего-то еще.')
84
  st.write('Также интересно понаблюдать за долями новых клиентов в данном распределении.')
85
 
86
- st.plotly_chart(tools.get_history_plot(dataset), use_container_width=True)
87
- st.markdown('_График интерактивный_')
88
  st.write('Абсолютное большинство клиентов тратят \$25-\$35 на покупки, но есть и малая доля тех, кто тратит более \$3.000')
89
  st.write('Интересный факт: все покупки более \$500 совершают только новые клиенты')
90
 
91
- st.subheader('Какие данные возьмем для проведения рекламной кампании?')
92
- clients_filter = st.radio('На каких клиентов будем воздействовать?', options=['Все', 'Только на новых', 'Только на старых'])
 
 
 
 
 
 
93
 
 
 
94
 
95
- st.write('Выберите класс клиентов, по объему денег, потраченных в прошлом году (history segments).')
 
96
  first_group = st.checkbox('$0-$100', value=True)
 
 
97
  second_group = st.checkbox('$100-$200', value=True)
 
 
98
  third_group = st.checkbox('$200-$350', value=True)
 
 
99
  fourth_group = st.checkbox('$350-$500', value=True)
 
 
100
  fifth_group = st.checkbox('$500-$750', value=True)
 
 
101
  sixth_group = st.checkbox('$750-$1.000', value=True)
 
 
102
  seventh_group = st.checkbox('$1.000+', value=True)
 
 
103
 
104
  st.write('Каких пользователей по почтовому коду выберем')
 
105
  surburban = st.checkbox('Surburban', value=True)
 
 
106
  urban = st.checkbox('Urban', value=True)
 
 
107
  rural = st.checkbox('Rural', value=True)
 
 
108
 
109
- recency = st.slider(label='Месяцев с момента покупки', min_value=int(dataset.recency.min()), max_value=int(dataset.recency.max()), value=(int(dataset.recency.min()), int(dataset.recency.max())))
 
110
 
 
111
  if not first_group and not second_group and not third_group and not fourth_group and not fifth_group and not sixth_group and not seventh_group:
112
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  else:
114
- st.button('Отфильтровать')
 
 
 
 
 
 
 
7
 
8
  dataset, target, treatment = tools.get_data()
9
 
10
+ data_train, data_val, treatment_train, treatment_val, target_train, target_val = tools.data_split(dataset, treatment, target)
11
+
12
  st.title('Uplift lab')
13
 
14
  st.markdown(
 
19
 
20
  Этот набор данных содержит 42 693 строк с данными клиентов, которые в последний раз совершали покупки в течение двенадцати месяцев.
21
 
22
+ Из данных уже отделена тестовая выборка в виде 30% записей клиентов, так что данных в предоставленной выборке будет меньше.
23
+
24
  Среди клиентов была проведена рекламная кампания с помощью _email_ рассылки:
25
  - 1/2 клиентов были выбраны случайным образом для получения электронного письма, рекламирующего женскую продукцию;
26
  - С оставшейся 1/2 коммуникацию не проводили.
 
32
  """
33
  )
34
  refresh = st.button('Обновить выборку')
35
+ title_subsample = data_train.sample(7)
36
  if refresh:
37
+ title_subsample = data_train.sample(7)
38
  st.dataframe(title_subsample, width=700)
39
+ st.write(f"Всего записей: {data_train.shape[0]}")
40
 
41
  st.write('Описание данных')
42
  st.markdown(
 
60
 
61
  with st.expander('Развернуть блок анализа данных'):
62
 
63
+ st.plotly_chart(tools.get_newbie_plot(data_train), use_container_width=True)
64
  st.write(f'В данных примерно одинаковое количество новых и "старых клиентов". '
65
+ f'Отношение новых клиентов к старым: {(data_train["newbie"] == 1).sum() / (data_train["newbie"] == 0).sum():.2f}')
66
 
67
+ st.plotly_chart(tools.get_zipcode_plot(data_train), use_container_width=True)
68
+ tmp_res = data_train.zip_code.value_counts(normalize=True) * 100
69
  st.write(f'Большинство клиентов из пригорода: {tmp_res["Surburban"]:.2f}%, из города: {tmp_res["Urban"]:.2f}% и из села: {tmp_res["Rural"]:.2f}%')
70
 
71
+ tmp_res = data_train.channel.value_counts(normalize=True) * 100
72
+ st.plotly_chart(tools.get_channel_plot(data_train), use_container_width=True)
73
  st.write(f'В прошлом году почти одинаковое количество клиентов покупало товары через телефон и сайт, {tmp_res["Phone"]:.2f}% и {tmp_res["Web"]:.2f}% соответственно,'
74
  f' а {tmp_res["Multichannel"]:.2f}% клиентов покупали товары воспользовавшись двумя платформами.')
75
 
76
+ tmp_res = data_train.history_segment.value_counts(normalize=True) * 100
77
+ st.plotly_chart(tools.get_history_segment_plot(data_train), use_container_width=True)
78
  st.write(f'Как мы видим, большинство пользователей относится к сегменту \$0-\$100 ({tmp_res[0]:.2f}%), второй и '
79
  f'третий по количеству пользователей сегменты \$100-\$200 ({tmp_res[1]:.2f}%) и \$200-\$350 ({tmp_res[2]:.2f}%).')
80
  st.write(f'К сегментам \$350-\$500 и \$500-\$750 относится {tmp_res[3]:.2f}% и {tmp_res[4]:.2f}% пользователей соответственно.')
81
  st.write(f'Меньше всего пользователей в сегментах \$750-\$1.000 ({tmp_res[-2]:.2f}%) и \$1.000+ ({tmp_res[-1]:.2f}%).')
82
 
83
+ tmp_res = list(data_train.recency.value_counts(normalize=True) * 100)
84
+ st.plotly_chart(tools.get_recency_plot(data_train), use_container_width=True)
85
  st.write(f'Большинство клиентов являются активными клиентами платформы, и совершали покупки в течен��е месяца ({tmp_res[0]:.2f}%)')
86
  st.write('Также заметно, что 9 и 10 месяцев назад, много клиентов совершали покупки. Это может свидетельствовать о проведении'
87
  'рекламной кампании в это время или чего-то еще.')
88
  st.write('Также интересно понаблюдать за долями новых клиентов в данном распределении.')
89
 
90
+ st.plotly_chart(tools.get_history_plot(data_train), use_container_width=True)
91
+ st.markdown('_График интерактивный. Двойной клик вернет в начальное состояние._')
92
  st.write('Абсолютное большинство клиентов тратят \$25-\$35 на покупки, но есть и малая доля тех, кто тратит более \$3.000')
93
  st.write('Интересный факт: все покупки более \$500 совершают только новые клиенты')
94
 
95
+ filters = {}
96
+
97
+ st.subheader('Выберем клиентов, которым отправим рекламу.')
98
+ newbie_filter = st.radio('Каким клиентам отправим рекламу?', options=['Всем', 'Только новым', 'Только старым'])
99
+ filters['newbie_filter'] = newbie_filter
100
+
101
+ channel_filter = st.radio('Канал, по которому клиент покупал в прошлом году', options=['Всем', 'Phone', 'Web', 'Multichannel'])
102
+ filters['channel_filter'] = channel_filter
103
 
104
+ mens_filter = st.radio('Клиенты, приобретавшие', options=['Любые товары', 'Мужские', 'Женские'])
105
+ filters['mens_filter'] = mens_filter
106
 
107
+ st.write('Выберите класс клиентов, по объему денег, потраченных в прошлом году (history segments)')
108
+ filters['history_segments'] = {}
109
  first_group = st.checkbox('$0-$100', value=True)
110
+ if first_group:
111
+ filters['history_segments']['1) $0 - $100'] = True
112
  second_group = st.checkbox('$100-$200', value=True)
113
+ if second_group:
114
+ filters['history_segments']['2) $100 - $200'] = True
115
  third_group = st.checkbox('$200-$350', value=True)
116
+ if third_group:
117
+ filters['history_segments']['3) $200 - $350'] = True
118
  fourth_group = st.checkbox('$350-$500', value=True)
119
+ if fourth_group:
120
+ filters['history_segments']['4) $350 - $500'] = True
121
  fifth_group = st.checkbox('$500-$750', value=True)
122
+ if fifth_group:
123
+ filters['history_segments']['5) $500 - $750'] = True
124
  sixth_group = st.checkbox('$750-$1.000', value=True)
125
+ if sixth_group:
126
+ filters['history_segments']['6) $750 - $1,000'] = True
127
  seventh_group = st.checkbox('$1.000+', value=True)
128
+ if seventh_group:
129
+ filters['history_segments']['7) $1,000 +'] = True
130
 
131
  st.write('Каких пользователей по почтовому коду выберем')
132
+ filters['zip_code'] = {}
133
  surburban = st.checkbox('Surburban', value=True)
134
+ if surburban:
135
+ filters['zip_code']['surburban'] = True
136
  urban = st.checkbox('Urban', value=True)
137
+ if urban:
138
+ filters['zip_code']['urban'] = True
139
  rural = st.checkbox('Rural', value=True)
140
+ if rural:
141
+ filters['zip_code']['rural'] = True
142
 
143
+ recency = st.slider(label='Месяцев с момента покупки', min_value=int(data_train.recency.min()), max_value=int(data_train.recency.max()), value=(int(data_train.recency.min()), int(data_train.recency.max())))
144
+ filters['recency'] = recency
145
 
146
+ disabled = False
147
  if not first_group and not second_group and not third_group and not fourth_group and not fifth_group and not sixth_group and not seventh_group:
148
+ st.error('Необходимо выбрать хотя бы один класс')
149
+ disabled = True
150
+ elif not surburban and not urban and not rural:
151
+ st.error('Необходимо выбрать хотя бы один почтовый индекс')
152
+ disabled = True
153
+
154
+ filter_data = st.button(label='Отфильтровать', disabled=disabled)
155
+
156
+ if filter_data:
157
+ filtered_dataset = tools.filter_data(data_train, filters)
158
+ if filtered_dataset is None:
159
+ st.error('Нет подходящих под выбранный фильтр клиентов. Поп��обуйте изменить фильтр.')
160
+ sample_size = 7 if filtered_dataset.shape[0] >= 7 else filtered_dataset.shape[0]
161
+ example = filtered_dataset.sample(sample_size)
162
+ st.write('Пример пользователей, которым будет отправлена реклама')
163
+ st.dataframe(example)
164
+ st.info(f'Количество клиентов, которым реклама будет отправлена: _**{filtered_dataset.shape[0]}**_ ({filtered_dataset.shape[0] / data_train.shape[0] * 100 :.2f}% от всех клиентов)')
165
  else:
166
+ # нельзя отправить рекламу, до фильтрации
167
+ disabled = True
168
+
169
+ send_promo = st.button('Отправить рекламу и посмотреть результат', disabled=disabled)
170
+ if send_promo:
171
+ pass
172
+