Spaces:
Runtime error
Runtime error
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)
|
|
|