Spaces:
Runtime error
Runtime error
mathiasleys
commited on
Commit
โข
a8905e8
1
Parent(s):
d35ddb1
Move explanation to Medium blog post
Browse files- app.py +7 -261
- assets/images/cannibalization.png +0 -0
- assets/images/dynamic_demand.gif +0 -3
- assets/images/flywheel_1.png +0 -0
- assets/images/flywheel_2.png +0 -0
- assets/images/flywheel_3.png +0 -0
- assets/images/gaussian_process.gif +0 -3
- assets/images/ideal_case_demand.png +0 -0
- assets/images/ideal_case_demand_fitted.png +0 -0
- assets/images/ideal_case_optimal_profit.png +0 -0
- assets/images/ideal_case_profit_curve.png +0 -0
- assets/images/posterior_demand.png +0 -0
- assets/images/posterior_demand_2.png +0 -0
- assets/images/posterior_demand_sample.png +0 -0
- assets/images/posterior_demand_sample_2.png +0 -0
- assets/images/posterior_profit.png +0 -0
- assets/images/posterior_profit_2.png +0 -0
- assets/images/posterior_profit_sample.png +0 -0
- assets/images/posterior_profit_sample_2.png +0 -0
- assets/images/realistic_demand.png +0 -0
- assets/images/realistic_demand_latent_curve.png +0 -0
- assets/images/updated_prices_demand.png +0 -0
app.py
CHANGED
@@ -1,15 +1,12 @@
|
|
1 |
"""Streamlit entrypoint"""
|
2 |
|
3 |
-
import base64
|
4 |
import time
|
5 |
|
6 |
import numpy as np
|
7 |
import streamlit as st
|
8 |
-
import sympy
|
9 |
|
10 |
from helpers.thompson_sampling import ThompsonSampler
|
11 |
|
12 |
-
eta, a, p, D, profit, var_cost, fixed_cost = sympy.symbols("eta a p D Profit varcost fixedcost")
|
13 |
np.random.seed(42)
|
14 |
|
15 |
st.set_page_config(
|
@@ -27,265 +24,12 @@ st.set_page_config(
|
|
27 |
st.title("Dynamic Pricing")
|
28 |
st.subheader("Setting optimal prices with Bayesian stats ๐")
|
29 |
|
30 |
-
# (0) Intro
|
31 |
-
st.header("Why care about dynamic pricing? ๐ญ")
|
32 |
-
|
33 |
-
st.markdown("""Dynamic pricing aims to actively adapt product prices based on insights about
|
34 |
-
customer behaviour. \n
|
35 |
-
This pricing strategy has proven exceptionally effective in a wide range of industries: from
|
36 |
-
e-commerce (e.g., eBay) to airlines (e.g., Delta Airlines) and from retail (e.g., Walmart) to
|
37 |
-
utilities (e.g., Tampa Electric) as it allows to **adjust prices to changes in demand patterns**.""")
|
38 |
-
st.markdown("""It is hardly surprising that recent times of extraordinary uncertainty and volatility
|
39 |
-
caused a surge in adoption of dynamic pricing strategies with an [estimated 21% of e-commerce
|
40 |
-
businesses reportedly already using dynamic pricing](https://www.statista.com/statistics/1174557/dynamic-pricing-ecommerce-companies-worldwide/)
|
41 |
-
and an additional 15% planning to adopt the strategy in the upcoming year.""")
|
42 |
-
st.markdown("""To find a big success story, we should look no further than Amazon who (on average)
|
43 |
-
change their products' prices once every 10 minutes. They attribute roughly [25% of their e-commerce
|
44 |
-
profits](https://dzone.com/articles/big-data-analytics-delivering-business-value-at-am)
|
45 |
-
to their pricing strategy.""")
|
46 |
-
st.markdown("""In what follows we'll discuss the working principles & challenges in the field of
|
47 |
-
dynamic pricing, followed by an interactive demo and some final considerations.""")
|
48 |
-
|
49 |
-
# (1) Basics
|
50 |
-
st.header("Back to basics ๐")
|
51 |
-
|
52 |
-
st.markdown("The beginning is usually a good place to start so we'll kick things off there.")
|
53 |
-
st.markdown("""The one crucial piece information we need in order to find the optimal price is
|
54 |
-
**how demand behaves over different price points**. \nIf we can make a decent guess of what we
|
55 |
-
can expect demand to be for a wide range of prices, we can figure out which price optimizes our
|
56 |
-
target (i.e., revenue, profit, ...).""")
|
57 |
-
st.markdown("""For the keen economists amongst you, this is beginning to sound a lot like a
|
58 |
-
**demand curve**.""")
|
59 |
-
|
60 |
-
st.markdown("""Estimating a demand curve, sounds easy enough right? \nLet's assume we have
|
61 |
-
demand with constant price elasticity; so a certain percent change in price will cause a
|
62 |
-
constant percent change in demand, independent of the price level. In economics, this is often used
|
63 |
-
as a proxy for demand curves in the wild.""")
|
64 |
-
st.markdown("So our demand data looks something like this:")
|
65 |
-
st.image("assets/images/ideal_case_demand.png")
|
66 |
-
st.markdown("""Alright now we can get out our trusted regression toolbox and fit a nice curve
|
67 |
-
through the data because we know that our constant-elasticity demand function has this form:""")
|
68 |
-
st.latex(sympy.latex(sympy.Eq(sympy.Function(D)(p), a*p**(-eta), evaluate=False)))
|
69 |
-
st.write("with shape parameter a and price elasticity ฮท")
|
70 |
-
st.image("assets/images/ideal_case_demand_fitted.png")
|
71 |
-
st.markdown("""Now that we have a reasonable estimate of our demand function, we can derive our
|
72 |
-
expected profit at different price points because we know the following holds:""")
|
73 |
-
st.latex(f"{profit} = {p}*{sympy.Function(D)(p)} - [{var_cost}*{sympy.Function(D)(p)} + {fixed_cost}]")
|
74 |
-
st.image("assets/images/ideal_case_profit_curve.png")
|
75 |
-
st.markdown("""Note that fixed costs (e.g., rent, insurance, etc.), per definition, don't vary when
|
76 |
-
demand or price changes. Therefore, fixed costs have no influence on the behavior of dynamic pricing
|
77 |
-
algorithms.""")
|
78 |
-
st.markdown("""Finally we can dust off our good old high-school math book and find the
|
79 |
-
price which we expect will optimize profit which was ultimately the goal of all this.""")
|
80 |
-
st.image("assets/images/ideal_case_optimal_profit.png")
|
81 |
-
st.markdown("""Voilร there you have it: we should price this product at 4.24 and we can expect
|
82 |
-
a bottom-line profit of 7.34""")
|
83 |
-
st.markdown("So can we kick back & relax now? \nWell, there are a few issues with what we just did.")
|
84 |
-
|
85 |
-
# (2) Dynamic demand curves
|
86 |
-
st.header("The demands they are a-changin' ๐ธ")
|
87 |
-
st.markdown("""We arrive at our first bit of bad news: unfortunately, you can't just estimate a
|
88 |
-
demand curve once and be done with it. \nWhy? Because demand is influenced by many factors (e.g.,
|
89 |
-
market trends, competitor actions, human behavior, etc.) that tend to change a lot over time.""")
|
90 |
-
st.write("Below you can see an (exaggerated) example of what we're talking about:")
|
91 |
-
|
92 |
-
with open("assets/images/dynamic_demand.gif", "rb") as file_:
|
93 |
-
contents = file_.read()
|
94 |
-
data_url = base64.b64encode(contents).decode("utf-8")
|
95 |
-
|
96 |
-
st.markdown(
|
97 |
-
f'<img src="data:image/gif;base64,{data_url}" alt="dynamic demand">',
|
98 |
-
unsafe_allow_html=True,
|
99 |
-
)
|
100 |
-
st.markdown("""Now, you may think we can solve this issue by periodically re-estimating the demand
|
101 |
-
curve. \nAnd you would be very right! But also very wrong as this leads us nicely to the
|
102 |
-
next issue.""")
|
103 |
-
|
104 |
-
# (3) Constrained data
|
105 |
-
st.header("Where are we getting this data anyways? ๐ฅ๏ธ")
|
106 |
-
st.markdown("""So far, we have assumed that we get (and keep getting) data on demand levels at
|
107 |
-
different price points. \n
|
108 |
-
Not only is this assumption **unrealistic**, it is also very **undesirable**""")
|
109 |
-
st.markdown("""Why? Because getting demand data on a wide spectrum of price points implies that
|
110 |
-
we are spending a significant amount of time setting prices that are either too high or too low! \n
|
111 |
-
Which is ironically exactly the opposite of what we set out to achieve.""")
|
112 |
-
st.markdown("In practice, our demand observations will rather look something like this:")
|
113 |
-
st.image("assets/images/realistic_demand.png")
|
114 |
-
st.markdown("""As we can see, we have tried three price points in the past (โฌ7.5, โฌ10 and โฌ11) and
|
115 |
-
collected demand data.""")
|
116 |
-
st.markdown("""On a side note: keep in mind that we still assume the same latent demand curve and
|
117 |
-
optimal price point of โฌ4.24 \n
|
118 |
-
So (for the sake of the example) we have been massively overpricing our product in the past.""")
|
119 |
-
st.image("assets/images/realistic_demand_latent_curve.png")
|
120 |
-
st.markdown("""This limited data brings along a major challenge in estimating the demand curve
|
121 |
-
though. \n
|
122 |
-
Intuitively, it makes sense that we can make a reasonable estimate of expected demand at โฌ8 or โฌ9,
|
123 |
-
given the observed demand at โฌ7.5 and โฌ10. \nBut can we extrapolate further to โฌ2 or โฌ20 with the
|
124 |
-
same reasonable confidence? Probably not.""")
|
125 |
-
st.markdown("""This is a nice example of a very well-known problem in statistics called the
|
126 |
-
**\"exploration-exploitation trade-off\"** \n
|
127 |
-
๐ **Exploration**: We want to explore the demand for a diverse enough range of price points
|
128 |
-
so that we can accurately estimate our demand curve. \n
|
129 |
-
๐ **Exploitation**: We want to exploit all the knowledge we have gained through exploring and
|
130 |
-
actually do what we set out to do: set our price at an optimal level.""")
|
131 |
-
|
132 |
-
# (4) Thompson sampling explanation
|
133 |
-
st.header("Enter: Thompson Sampling ๐")
|
134 |
-
st.markdown("""As we mentioned, this is a well-known problem in statistics. So luckily for us,
|
135 |
-
there is a pretty neat solution in the form of **Thompson sampling**!""")
|
136 |
-
st.markdown("""Basically instead of estimating one demand function based on the data available to
|
137 |
-
us, we will estimate a probability distribution of demand functions or simply put, for every
|
138 |
-
possible demand function that fits our functional form (i.e. constant elasticity)
|
139 |
-
we will estimate the probability that it is the correct one, given our data.""")
|
140 |
-
st.markdown("""Or mathematically speaking, we will place a prior distribution on the parameters
|
141 |
-
that define our demand function and update these priors to posterior distributions via Bayes rule,
|
142 |
-
thus obtaining a posterior distribution for our demand function""")
|
143 |
-
st.markdown("""Thompson sampling then entails just sampling a demand function out of this
|
144 |
-
distribution, calculating the optimal price given this demand function, observing demand for this
|
145 |
-
new price point and using this information to refine our demand function estimates.""")
|
146 |
-
st.image("assets/images/flywheel_1.png")
|
147 |
-
st.markdown("""So: \n
|
148 |
-
๐ When we are **less certain** of our estimates, we will sample more diverse demand functions,
|
149 |
-
which means that we will also explore more diverse price points. Thus, we will **explore**. \n
|
150 |
-
๐ When we are **more certain** of our estimates, we will sample a demand function close to
|
151 |
-
the real one & set a price close to the optimal price more often. Thus, we will **exploit**.""")
|
152 |
-
|
153 |
-
st.markdown("""With that said, we'll take another look at our constrained data and see whether
|
154 |
-
Thompson sampling gets us any closer to the optimal price of โฌ4.24""")
|
155 |
-
st.image("assets/images/realistic_demand_latent_curve.png")
|
156 |
-
st.markdown("""Let's start working our mathemagic: \n
|
157 |
-
We'll start off by placing semi-informed priors on the parameters that make up our
|
158 |
-
demand function.""")
|
159 |
-
|
160 |
-
st.latex(f"{sympy.latex(a)} \sim N(ฮผ=0,ฯ=2)")
|
161 |
-
st.latex(f"{sympy.latex(eta)} \sim N(ฮผ=0.5,ฯ=0.5)")
|
162 |
-
st.latex("sd \sim Exp(\lambda=1)")
|
163 |
-
st.latex(f"{sympy.latex(D)}|P=p \sim N(ฮผ={sympy.latex(a*p**(-eta))},ฯ=sd)")
|
164 |
-
|
165 |
-
st.markdown("""These priors are semi-informed because we have the prior knowledge that
|
166 |
-
price elasticity is most likely between 0 and 1. As for the other parameters, we have little
|
167 |
-
knowledge about them so we can place a pretty uninformative prior.""")
|
168 |
-
st.markdown("If that made sense to you, great. If it didn't, don't worry about it")
|
169 |
-
|
170 |
-
st.markdown("""Now that are priors are taken care of, we can update these beliefs by incorporating
|
171 |
-
the data at the โฌ7.5, โฌ10 and โฌ11 price levels we have available to us.""")
|
172 |
-
st.markdown("The resulting demand & profit curve distributions look a little something like this:")
|
173 |
-
st.image(["assets/images/posterior_demand.png", "assets/images/posterior_profit.png"])
|
174 |
-
|
175 |
-
st.markdown("""It's time to sample one demand curve out of this posterior distribution. \n
|
176 |
-
The lucky curve is:""")
|
177 |
-
st.image("assets/images/posterior_demand_sample.png")
|
178 |
-
st.markdown("This results in the following expected profit curve")
|
179 |
-
st.image("assets/images/posterior_profit_sample.png")
|
180 |
-
st.markdown("""And eventually we arrive at a new price: โฌ5.25! Which is indeed considerably closer
|
181 |
-
to the actual optimal price of โฌ4.24""")
|
182 |
-
st.markdown("Now that we have our first updated price point, why stop there?")
|
183 |
-
st.markdown("""With \"pure\" Thompson sampling, we would sample a new demand curve (and thus price
|
184 |
-
point) out of the posterior distribution every time. But since we are mainly interested in seeing
|
185 |
-
the convergence behavior of Thompson sampling, let's simulate 10 demand points at this fixed โฌ5.25
|
186 |
-
price point.""")
|
187 |
-
st.image("assets/images/updated_prices_demand.png")
|
188 |
-
st.markdown("""We know the drill by now. \n
|
189 |
-
Let's recalculate our posteriors with this extra information.""")
|
190 |
-
st.image(["assets/images/posterior_demand_2.png", "assets/images/posterior_profit_2.png"])
|
191 |
-
st.markdown("""We immediately notice that the demand (and profit) posteriors are much less spread
|
192 |
-
apart this time around which implies that we are more confident in our predictions.""")
|
193 |
-
st.markdown("Now, we can sample just one curve from the distribution.")
|
194 |
-
st.image(["assets/images/posterior_demand_sample_2.png", "assets/images/posterior_profit_sample_2.png"])
|
195 |
-
st.markdown("""And finally we arrive at a price point of โฌ4.04 which is eerily close to
|
196 |
-
the actual optimum of โฌ4.24""")
|
197 |
-
|
198 |
-
# (5) Extra topics
|
199 |
-
st.header("Some things to think about ๐ค")
|
200 |
-
|
201 |
-
st.markdown("""Because we have purposefully kept the example above quite simple, you may still be
|
202 |
-
wondering what happens when added complexities show up. \n
|
203 |
-
Let's discuss some of those concerns FAQ-style:""")
|
204 |
-
|
205 |
-
st.subheader("๐ Isn't this constant-elasticity model a bit too simple to work in practice?")
|
206 |
-
st.markdown("Brief answer: usually yes it is.")
|
207 |
-
st.markdown("""Luckily, more flexible methods exist. \n
|
208 |
-
We would recommend to use Gaussian Processes. We won't go into how these work here but the main idea
|
209 |
-
is that it doesn't impose a restrictive functional form onto the demand function but rather lets
|
210 |
-
the data speak for itself.""")
|
211 |
-
|
212 |
-
with open("assets/images/gaussian_process.gif", "rb") as file_:
|
213 |
-
contents = file_.read()
|
214 |
-
data_url = base64.b64encode(contents).decode("utf-8")
|
215 |
-
|
216 |
-
st.markdown(
|
217 |
-
f'<img src="data:image/gif;base64,{data_url}" alt="gaussian process">',
|
218 |
-
unsafe_allow_html=True,
|
219 |
-
)
|
220 |
-
st.markdown("""If you do want to learn more, we recommend these links:
|
221 |
-
[1](https://distill.pub/2019/visual-exploration-gaussian-processes/),
|
222 |
-
[2](https://thegradient.pub/gaussian-process-not-quite-for-dummies/),
|
223 |
-
[3](https://sidravi1.github.io/blog/2018/05/15/latent-gp-and-binomial-likelihood)""")
|
224 |
-
|
225 |
-
st.subheader("""๐ Price optimization is much more complex than just optimizing a simple profit function?""")
|
226 |
-
st.markdown("""It sure is. In reality, there are many added complexities that come into play, such
|
227 |
-
as inventory/capacity constraints, complex cost structures, ...""")
|
228 |
-
st.markdown("""The nice thing about our setup is that it consists of three components that you can
|
229 |
-
change pretty much independently from each other. \n
|
230 |
-
This means that you can make the price optimization pillar arbitrarily custom/complex. As long as
|
231 |
-
it takes in a demand function and spits out a price.""")
|
232 |
-
st.image("assets/images/flywheel_2.png")
|
233 |
-
st.markdown("You can tune the other two steps as much as you like too.")
|
234 |
-
st.image("assets/images/flywheel_3.png")
|
235 |
-
|
236 |
-
st.subheader("๐ Changing prices has a huge impact. How can I mitigate this during experimentation?")
|
237 |
-
st.markdown("There are a few things we can do to minimize risk:")
|
238 |
-
st.markdown("""๐ **A/B testing**: You can do a gradual roll-out of the new pricing system where a
|
239 |
-
small (but increasing) percentage of your transactions are based on this new system. This allows you
|
240 |
-
to start small & track/grow the impact over time.""")
|
241 |
-
st.markdown("""๐ **Limit products**: Similarly to A/B testing, you can also segment on the
|
242 |
-
product-level. For instance, you can start gradually rolling out dynamic pricing for one product
|
243 |
-
type and extend this over time.""")
|
244 |
-
st.markdown("""๐ **Bound price range**: Theoretically, Thompson sampling in its purest form can
|
245 |
-
lead to any arbitrary price point (albeit with an increasingly low probability). In order to limit
|
246 |
-
the risk here, you can simply place a upper/lower bound on the price range you are comfortable
|
247 |
-
experimenting in.""")
|
248 |
-
st.markdown("""On top of all this, Bayesian methods (by design) explicitly quantify uncertainty.
|
249 |
-
This allows you to have a very concrete view on the variance of our demand estimates""")
|
250 |
-
|
251 |
-
st.subheader("๐ What if I have multiple products that can cannibalize each other?")
|
252 |
-
st.markdown("Here it really depends")
|
253 |
-
st.markdown("""๐ **If you have a handful of products**, we can simply reformulate our objective while
|
254 |
-
keeping our methods analogous. \n
|
255 |
-
Instead of tuning one price to optimize profit for the demand function of one product, we tune N
|
256 |
-
prices to optimize profit for the joint demand function of N products. This joint demand function
|
257 |
-
can then account for correlations in demand within products.""")
|
258 |
-
st.markdown("""๐ **If you have hundreds, thousands or more products**, we're sure you can imagine that
|
259 |
-
the procedure described above becomes increasingly infeasible. \n
|
260 |
-
A practical alternative is to group substitutable products into "baskets" and define the "price of
|
261 |
-
the basket" as the average price of all products in the basket. \n
|
262 |
-
If we assume that the products in baskets are subtitutable but the products in different baskets are
|
263 |
-
not, we can optimize basket prices indepedently from one another. \n
|
264 |
-
Finally, if we also assume that cannibalization remains constant if the ratio of prices remains
|
265 |
-
constant, we can calculate individual product prices as a fixed ratio of its basket price. \n""")
|
266 |
-
st.markdown("""For example, if a "burger basket" consists of a hamburger (โฌ1) and a cheeseburger
|
267 |
-
(โฌ3), then the "burger price" is ((โฌ1 + โฌ3) / 2 =) โฌ2. So a hamburger costs 50% of the burger price
|
268 |
-
and a cheeseburger costs 150% of the burger price. \n
|
269 |
-
If we change the burger's price to โฌ3, a hamburger will cost (50% * โฌ3 =) โฌ1.5 and a cheeseburger
|
270 |
-
will cost (150% * โฌ3 =) โฌ4.5 because we assume that the cannibalization effect between hamburgers &
|
271 |
-
cheeseburgers is the same when hamburgers cost โฌ1 & cheeseburgers cost โฌ3 and when hamburgers cost
|
272 |
-
โฌ1.5 & cheeseburgers cost โฌ4.5""")
|
273 |
-
st.image("assets/images/cannibalization.png")
|
274 |
-
|
275 |
-
st.subheader("๐ Is dynamic pricing even relevant for slow-selling products?")
|
276 |
-
st.markdown("""The boring answer is that it depends. It depends on how dynamic the market is, the
|
277 |
-
quality of the prior information, ...""")
|
278 |
-
st.markdown("""But obviously this isn't very helpful. \nIn general, we notice that you can already
|
279 |
-
get quite far with limited data, especially if you have an accurate prior belief on how the demand
|
280 |
-
likely behaves.""")
|
281 |
-
st.markdown("""For reference, in our simple example where we showed a Thompson sampling update, we
|
282 |
-
were already able to gain a lot of confidence in our estimates with just 10 extra demand
|
283 |
-
observations.""")
|
284 |
-
|
285 |
-
# (6) Thompson sampling demo
|
286 |
st.header("Demo time ๐ฎ")
|
287 |
-
st.markdown("
|
288 |
-
|
|
|
|
|
|
|
289 |
๐ As you increase price elasticity, the demand becomes more sensitive to price changes and thus the
|
290 |
profit-optimizing price becomes lower (& vice versa). \n
|
291 |
๐ As you decrease price elasticity, our demand observations at โฌ7.5, โฌ10 and โฌ11 become
|
@@ -293,6 +37,8 @@ increasingly larger and increasingly more variable (as their variance is a const
|
|
293 |
absolute value). This causes our demand posterior to become increasingly wider and thus Thompson
|
294 |
sampling will lead to more exploration.
|
295 |
""")
|
|
|
|
|
296 |
|
297 |
thompson_sampler = ThompsonSampler()
|
298 |
demo_button = st.checkbox(
|
|
|
1 |
"""Streamlit entrypoint"""
|
2 |
|
|
|
3 |
import time
|
4 |
|
5 |
import numpy as np
|
6 |
import streamlit as st
|
|
|
7 |
|
8 |
from helpers.thompson_sampling import ThompsonSampler
|
9 |
|
|
|
10 |
np.random.seed(42)
|
11 |
|
12 |
st.set_page_config(
|
|
|
24 |
st.title("Dynamic Pricing")
|
25 |
st.subheader("Setting optimal prices with Bayesian stats ๐")
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
st.header("Demo time ๐ฎ")
|
28 |
+
st.markdown("""In this demo you will see \n
|
29 |
+
๐ How Bayesian demand function estimates are created based on sales data \n
|
30 |
+
๐ How Thompson sampling will generate concrete price points from these Bayesian estimates \n
|
31 |
+
๐ The impact of price elasticity on Bayesian demand estimation""")
|
32 |
+
st.markdown("""You will notice: \n
|
33 |
๐ As you increase price elasticity, the demand becomes more sensitive to price changes and thus the
|
34 |
profit-optimizing price becomes lower (& vice versa). \n
|
35 |
๐ As you decrease price elasticity, our demand observations at โฌ7.5, โฌ10 and โฌ11 become
|
|
|
37 |
absolute value). This causes our demand posterior to become increasingly wider and thus Thompson
|
38 |
sampling will lead to more exploration.
|
39 |
""")
|
40 |
+
st.markdown("""If you are looking for more insights into how dynamic pricing is done in practice,
|
41 |
+
check out our blog post here: https://medium.com/ml6team/dynamic-pricing-in-practice-99fe2216a93d""")
|
42 |
|
43 |
thompson_sampler = ThompsonSampler()
|
44 |
demo_button = st.checkbox(
|
assets/images/cannibalization.png
DELETED
Binary file (66.5 kB)
|
|
assets/images/dynamic_demand.gif
DELETED
Git LFS Details
|
assets/images/flywheel_1.png
DELETED
Binary file (46.3 kB)
|
|
assets/images/flywheel_2.png
DELETED
Binary file (67.6 kB)
|
|
assets/images/flywheel_3.png
DELETED
Binary file (112 kB)
|
|
assets/images/gaussian_process.gif
DELETED
Git LFS Details
|
assets/images/ideal_case_demand.png
DELETED
Binary file (8.93 kB)
|
|
assets/images/ideal_case_demand_fitted.png
DELETED
Binary file (14 kB)
|
|
assets/images/ideal_case_optimal_profit.png
DELETED
Binary file (13.3 kB)
|
|
assets/images/ideal_case_profit_curve.png
DELETED
Binary file (10.9 kB)
|
|
assets/images/posterior_demand.png
DELETED
Binary file (28.3 kB)
|
|
assets/images/posterior_demand_2.png
DELETED
Binary file (29.6 kB)
|
|
assets/images/posterior_demand_sample.png
DELETED
Binary file (12 kB)
|
|
assets/images/posterior_demand_sample_2.png
DELETED
Binary file (12.7 kB)
|
|
assets/images/posterior_profit.png
DELETED
Binary file (31 kB)
|
|
assets/images/posterior_profit_2.png
DELETED
Binary file (30.8 kB)
|
|
assets/images/posterior_profit_sample.png
DELETED
Binary file (13.2 kB)
|
|
assets/images/posterior_profit_sample_2.png
DELETED
Binary file (14.3 kB)
|
|
assets/images/realistic_demand.png
DELETED
Binary file (6.9 kB)
|
|
assets/images/realistic_demand_latent_curve.png
DELETED
Binary file (10.1 kB)
|
|
assets/images/updated_prices_demand.png
DELETED
Binary file (7.15 kB)
|
|