mathiasleys commited on
Commit
e144a1c
·
1 Parent(s): e819afa

Fix text typos

Browse files
Files changed (1) hide show
  1. app.py +34 -34
app.py CHANGED
@@ -31,31 +31,30 @@ st.subheader("Setting optimal prices with Bayesian stats 📈")
31
  st.header("Let's start with the basics 🏁")
32
 
33
  st.markdown("The beginning is usually a good place to start so we'll kick things off there.")
34
- st.markdown("""The one crucial piece information we need to find the optimal price is
35
- **how demand behaves over different price points**. \nIf we can get make a decent guess of what we
36
- can expect demand to be for a wide range of prices, we can figure out which price optimizes our target
37
- (i.e., revenue, profit, ...).""")
38
  st.markdown("""For the keen economists amongst you, this is beginning to sound a lot like a
39
  **demand curve**.""")
40
 
41
  st.markdown("""Estimating a demand curve, sounds easy enough right? \nLet's assume we have
42
  demand with constant price elasticity; so a certain percent change in price will cause a
43
- constant percent change in demand, independent of the price level. This is often seen as a
44
- reasonable proxy for demand curves in the wild.""")
45
- st.markdown("So our data will look something like this:")
46
  st.image("assets/images/ideal_case_demand.png")
47
  st.markdown("""Alright now we can get out our trusted regression toolbox and fit a nice curve
48
- through the data because we know that our constant-elasticity demand function looks something
49
- like this:""")
50
  st.latex(sympy.latex(sympy.Eq(sympy.Function(D)(p), a*p**(-eta), evaluate=False)))
51
  st.write("with shape parameter a and price elasticity η")
52
  st.image("assets/images/ideal_case_demand_fitted.png")
53
- st.markdown("""Now that we have a reasonable estimate of our demand, we can derive our expected
54
- profit at different price points because we know the following holds:""")
55
  st.latex(f"{profit} = {p}*{sympy.Function(D)(p)} - [{var_cost}*{sympy.Function(D)(p)} + {fixed_cost}]")
56
  st.image("assets/images/ideal_case_profit_curve.png")
57
- st.markdown("""Finally we can dust off our good old-fashioned high-school math and find the
58
- price which we expect will optimize profit which is ultimately the goal of all this.""")
59
  st.image("assets/images/ideal_case_optimal_profit.png")
60
  st.markdown("""Voilà there you have it: we should price this product at 4.24 and we can expect
61
  a bottom-line profit of 7.34""")
@@ -63,9 +62,9 @@ st.markdown("So can we kick back & relax now? \nWell, there are a few issues wi
63
 
64
  # (2) Dynamic demand curves
65
  st.header("The demands they are a-changin' 🎸")
66
- st.markdown("""We've got a first bit of bad news: unfortunately, you can't just estimate a demand
67
- curve once and be done with it. \nWhy? Because demand is influenced by many factors (e.g., market
68
- trends, competitor actions, human behavior, etc.) that tend to change a lot over time.""")
69
  st.write("Below you can see an (exaggerated) example of what we're talking about:")
70
 
71
  with open("assets/images/dynamic_demand.gif", "rb") as file_:
@@ -86,24 +85,23 @@ st.markdown("""So far, we have assumed that we get (and keep getting) data on de
86
  different price points. \n
87
  Not only is this assumption **unrealistic**, it is also very **undesirable**""")
88
  st.markdown("""Why? Because getting demand data on a wide spectrum of price points implies that
89
- we are spending a significant amount of time setting prices at levels that are either too high or
90
- too low! \n
91
  Which is ironically exactly the opposite of what we set out to achieve.""")
92
- st.markdown("In practice, our demand will rather look something like this:")
93
  st.image("assets/images/realistic_demand.png")
94
- st.markdown("""As we can see, we have tried three price points in the past (€7.5, €10 and €11) for
95
- which we have collected demand data.""")
96
  st.markdown("""On a side note: keep in mind that we still assume the same latent demand curve and
97
  optimal price point of €4.24 \n
98
- So (for the sake of the example) we have been massively overpricing our product in the past""")
99
  st.image("assets/images/realistic_demand_latent_curve.png")
100
- st.markdown("""This constrained data brings along a major challenge in estimating the demand curve
101
  though. \n
102
  Intuitively, it makes sense that we can make a reasonable estimate of expected demand at €8 or €9,
103
  given the observed demand at €7.5 and €10. \nBut can we extrapolate further to €2 or €20 with the
104
- same reasonable confidence?""")
105
  st.markdown("""This is a nice example of a very well-known problem in statistics called the
106
- **\"exploration-exploitation\" trade-off** \n
107
  👉 **Exploration**: We want to explore the demand for a diverse enough range of price points
108
  so that we can accurately estimate our demand curve. \n
109
  👉 **Exploitation**: We want to exploit all the knowledge we have gained through exploring and
@@ -161,17 +159,17 @@ st.markdown("""And eventually we arrive at a new price: €5.25! Which is indeed
161
  to the actual optimal price of €4.24""")
162
  st.markdown("""Now that we have our first updated price point, why stop there? Let's simulate 10
163
  demand data points at this price point from out latent demand curve and check whether Thompson
164
- sampling will edge us even closer to that optimal €4.24 point""")
165
  st.image("assets/images/updated_prices_demand.png")
166
- st.markdown("""We know the drill by down. \n
167
  Let's recalculate our posteriors with this extra information.""")
168
  st.image(["assets/images/posterior_demand_2.png", "assets/images/posterior_profit_2.png"])
169
  st.markdown("""We immediately notice that the demand (and profit) posteriors are much less spread
170
- apart this time around which implies that we are more confident in our predictions""")
171
- st.markdown("Now, we can sample just one curve from the distribution")
172
  st.image(["assets/images/posterior_demand_sample_2.png", "assets/images/posterior_profit_sample_2.png"])
173
  st.markdown("""And finally we arrive at a price point of €4.44 which is eerily close to
174
- the actual optimum of €4.24.""")
175
 
176
  # (5) Thompson sampling demo
177
  st.header("Demo time 🎮")
@@ -187,7 +185,7 @@ elasticity = st.slider(
187
  key="latent_elasticity",
188
  min_value=0.05,
189
  max_value=0.95,
190
- value=0.15,
191
  step=0.05,
192
  )
193
  while demo_button:
@@ -221,9 +219,11 @@ st.markdown("""If you do want to learn more, we recommend these links:
221
  [2](https://thegradient.pub/gaussian-process-not-quite-for-dummies/),
222
  [3](https://sidravi1.github.io/blog/2018/05/15/latent-gp-and-binomial-likelihood)""")
223
 
224
- st.subheader("""👉 Price optimization is much more complex than just finding the point that maximizes a simple profit function. What about inventory constraints, complex cost structures, ...?""")
225
- st.markdown("""It sure is but the nice thing about our setup is that it consists of three
226
- components that you can change pretty much independently from each other. \n
 
 
227
  This means that you can make the price optimization pillar arbitrarily custom/complex. As long as
228
  it takes in a demand function and spits out a price.""")
229
  st.image("assets/images/flywheel_2.png")
 
31
  st.header("Let's start with the basics 🏁")
32
 
33
  st.markdown("The beginning is usually a good place to start so we'll kick things off there.")
34
+ st.markdown("""The one crucial piece information we need in order to find the optimal price is
35
+ **how demand behaves over different price points**. \nIf we can make a decent guess of what we
36
+ can expect demand to be for a wide range of prices, we can figure out which price optimizes our
37
+ target (i.e., revenue, profit, ...).""")
38
  st.markdown("""For the keen economists amongst you, this is beginning to sound a lot like a
39
  **demand curve**.""")
40
 
41
  st.markdown("""Estimating a demand curve, sounds easy enough right? \nLet's assume we have
42
  demand with constant price elasticity; so a certain percent change in price will cause a
43
+ constant percent change in demand, independent of the price level. In economics, this is often used
44
+ as a proxy for demand curves in the wild.""")
45
+ st.markdown("So our demand data looks something like this:")
46
  st.image("assets/images/ideal_case_demand.png")
47
  st.markdown("""Alright now we can get out our trusted regression toolbox and fit a nice curve
48
+ through the data because we know that our constant-elasticity demand function has this form:""")
 
49
  st.latex(sympy.latex(sympy.Eq(sympy.Function(D)(p), a*p**(-eta), evaluate=False)))
50
  st.write("with shape parameter a and price elasticity η")
51
  st.image("assets/images/ideal_case_demand_fitted.png")
52
+ st.markdown("""Now that we have a reasonable estimate of our demand function, we can derive our
53
+ expected profit at different price points because we know the following holds:""")
54
  st.latex(f"{profit} = {p}*{sympy.Function(D)(p)} - [{var_cost}*{sympy.Function(D)(p)} + {fixed_cost}]")
55
  st.image("assets/images/ideal_case_profit_curve.png")
56
+ st.markdown("""Finally we can dust off our good old high-school math book and find the
57
+ price which we expect will optimize profit which was ultimately the goal of all this.""")
58
  st.image("assets/images/ideal_case_optimal_profit.png")
59
  st.markdown("""Voilà there you have it: we should price this product at 4.24 and we can expect
60
  a bottom-line profit of 7.34""")
 
62
 
63
  # (2) Dynamic demand curves
64
  st.header("The demands they are a-changin' 🎸")
65
+ st.markdown("""We arrive at our first bit of bad news: unfortunately, you can't just estimate a
66
+ demand curve once and be done with it. \nWhy? Because demand is influenced by many factors (e.g.,
67
+ market trends, competitor actions, human behavior, etc.) that tend to change a lot over time.""")
68
  st.write("Below you can see an (exaggerated) example of what we're talking about:")
69
 
70
  with open("assets/images/dynamic_demand.gif", "rb") as file_:
 
85
  different price points. \n
86
  Not only is this assumption **unrealistic**, it is also very **undesirable**""")
87
  st.markdown("""Why? Because getting demand data on a wide spectrum of price points implies that
88
+ we are spending a significant amount of time setting prices that are either too high or too low! \n
 
89
  Which is ironically exactly the opposite of what we set out to achieve.""")
90
+ st.markdown("In practice, our demand observations will rather look something like this:")
91
  st.image("assets/images/realistic_demand.png")
92
+ st.markdown("""As we can see, we have tried three price points in the past (€7.5, €10 and €11) and
93
+ collected demand data.""")
94
  st.markdown("""On a side note: keep in mind that we still assume the same latent demand curve and
95
  optimal price point of €4.24 \n
96
+ So (for the sake of the example) we have been massively overpricing our product in the past.""")
97
  st.image("assets/images/realistic_demand_latent_curve.png")
98
+ st.markdown("""This limited data brings along a major challenge in estimating the demand curve
99
  though. \n
100
  Intuitively, it makes sense that we can make a reasonable estimate of expected demand at €8 or €9,
101
  given the observed demand at €7.5 and €10. \nBut can we extrapolate further to €2 or €20 with the
102
+ same reasonable confidence? Probably not.""")
103
  st.markdown("""This is a nice example of a very well-known problem in statistics called the
104
+ **\"exploration-exploitation trade-off\"** \n
105
  👉 **Exploration**: We want to explore the demand for a diverse enough range of price points
106
  so that we can accurately estimate our demand curve. \n
107
  👉 **Exploitation**: We want to exploit all the knowledge we have gained through exploring and
 
159
  to the actual optimal price of €4.24""")
160
  st.markdown("""Now that we have our first updated price point, why stop there? Let's simulate 10
161
  demand data points at this price point from out latent demand curve and check whether Thompson
162
+ sampling will edge us even closer to that optimal €4.24 point.""")
163
  st.image("assets/images/updated_prices_demand.png")
164
+ st.markdown("""We know the drill by now. \n
165
  Let's recalculate our posteriors with this extra information.""")
166
  st.image(["assets/images/posterior_demand_2.png", "assets/images/posterior_profit_2.png"])
167
  st.markdown("""We immediately notice that the demand (and profit) posteriors are much less spread
168
+ apart this time around which implies that we are more confident in our predictions.""")
169
+ st.markdown("Now, we can sample just one curve from the distribution.")
170
  st.image(["assets/images/posterior_demand_sample_2.png", "assets/images/posterior_profit_sample_2.png"])
171
  st.markdown("""And finally we arrive at a price point of €4.44 which is eerily close to
172
+ the actual optimum of €4.24""")
173
 
174
  # (5) Thompson sampling demo
175
  st.header("Demo time 🎮")
 
185
  key="latent_elasticity",
186
  min_value=0.05,
187
  max_value=0.95,
188
+ value=0.25,
189
  step=0.05,
190
  )
191
  while demo_button:
 
219
  [2](https://thegradient.pub/gaussian-process-not-quite-for-dummies/),
220
  [3](https://sidravi1.github.io/blog/2018/05/15/latent-gp-and-binomial-likelihood)""")
221
 
222
+ st.subheader("""👉 Price optimization is much more complex than just optimizing a simple profit function?""")
223
+ st.markdown("""It sure is. In reality, there are many added complexities that come into play, such
224
+ as inventory/capacity constraints, complex cost structures, ...""")
225
+ st.markdown("""The nice thing about our setup is that it consists of three components that you can
226
+ change pretty much independently from each other. \n
227
  This means that you can make the price optimization pillar arbitrarily custom/complex. As long as
228
  it takes in a demand function and spits out a price.""")
229
  st.image("assets/images/flywheel_2.png")