Different demand functions and optimal price estimation in R

By Yuri Fonseca

Demand models

In the previous post about pricing optimization (link here), we discussed a little about linear demand and how to estimate optimal prices in that case. In this post we are going to compare three different types of demand models for homogeneous products and how to find optimal prices for each one of them.

For the linear model, demand is given by:

\displaystyle d(p) = \alpha p + \beta,

where \alpha is the slope of the curve and \beta the intercept. For the linear model, the elasticity goes from zero to infinity. Another very common demand model is the constant-elasticity model, given by:

\displaystyle \ln d(p) = \alpha \ln p + \beta,

\displaystyle d(p) = d_0 e^\beta p^\alpha = Cp^\alpha,

where \alpha is the elasticity of the demand and C is a scale factor. A much more interesting demand curve is given by the logistic/sigmoide function:

\displaystyle d(p) = C\frac{e^{\alpha p + \beta}}{1 + e^{\alpha p + \beta}} = \frac{C}{1+e^{-\alpha(p - p_0)}},

where C is a scale factor and \alpha measures price sensitivity. We also can observe p_0 = -\alpha/\beta as the inflection point of the demand.

Some books changes the signs of the coefficients using the assumption that \alpha is a positive constant and using a minus sign in front of it. However, it does not change the estimation procedure or final result, it is just a matter of convenience. Here, we expect \alpha to be negative in the three models.

In the Figure below we can check a comparison among the shapes of the demand models:


linear = function(p, alpha, beta) alpha*p + beta
constant_elast = function(p, alpha, beta) exp(alpha*log(p)+beta)
logistic = function(p, c, alpha, p0) c/(1+exp(-alpha*(p-p0)))

p = seq(1, 100)
y1 = linear(p, -1, 100)
y2 = constant_elast(p, -.5, 4.5)
y3 = logistic(p, 100, -.2, 50)

df = data.frame('Prices' = p, 'Linear' = y1, 'Constant_elast' = y2, 'Logistic' = y3)
df.plot = melt(df, id = 'Prices') %>% set_colnames(c('Prices', 'Model', 'Demand'))

ggplot(df.plot) + aes(x = Prices, y = Demand) +
  geom_line(color = 'blue', alpha = .6, lwd = 1) +

plot of chunk demand_models

Of course that in practice prices does not change between 1 and 100, but the idea is to show the main differences in the shape of the models.

All the models presented above have positive and negative points. Although local linear approximation may be reasonable for small changes in prices, sometimes this assumption is too strong and does not capture the correct sensitivity of bigger price changes. In the constant elasticity model, even though it is a non-linear relationship between demand and price, the constant elasticity assumption might be too restrictive. Moreover, it tends to over estimate the demand for lower and bigger prices. In a fist moment, I would venture to say that the logistic function is the most robust and realistic among the three types.

Pricing with demand models

In a general setting, one have for the total profit function:

\displaystyle L(p) = d(p)(p-c),

where, L gives the profit, d is the demand function that depends of the price and c is the marginal cost. Taking the derivative with respect to price we have:

\displaystyle L'(p) = d'(p)(p - c) + d(p).

Making L'(p) = 0 to calculate the optimum price (first order condition), we have:

\displaystyle d'(p^\star)(p^\star - c) + d(p^\star) = 0
\displaystyle d'(p^\star)p^\star + d(p^\star) = d'(p^\star)c,

which is the famous condition that in the optimal price, marginal cost equals marginal revenue. Next, let’s see how to calculate the optimum prices for each demand functions.

Linear model

For the linear model d'(p) = \alpha. Hence:

\displaystyle d'(p^\star)p + d(p^\star) = d'(p^\star)c,
\displaystyle \alpha p^\star + \alpha p^\star + \beta = \alpha c,
\displaystyle p^\star = \frac{\alpha c - \beta}{2\alpha}.



# Synthetic data
p = seq(80,130)
d = linear(p, alpha = -1.5, beta = 200) + rnorm(sd = 5, length(p))
c = 75
profit = d*(p-c)

# Fit of the demand model
model1 = lm(d~p)
profit.fitted = model1$fitted.values*(p - c)

# Pricing Optimization
alpha = model1$coefficients[2]
beta = model1$coefficients[1]
p.max.profit = (alpha*c - beta)/(2*alpha)

# Plots
df.linear = data.frame('Prices' = p, 'Demand' = d,
                       'Profit.fitted' = profit.fitted, 'Profit' = profit)

ggplot(select(df.linear, Prices, Demand)) + aes(x = Prices, y = Demand) +
  geom_point() + geom_smooth(method = lm)

plot of chunk profit_linear_demand

ggplot(select(df.linear, Prices, Profit)) + aes(x = Prices, y = Profit) +
  geom_point() + geom_vline(xintercept = p.max.profit, lty = 2) +
  geom_line(data = df.linear, aes(x = Prices, y = Profit.fitted), color = 'blue')

plot of chunk profit_linear_demand

Constant elasticity model

For the constant elasticity model, since \lim_{\Delta \rightarrow 0}\frac{\Delta D}{\Delta p} = d'(p), we have that:

\displaystyle \epsilon = \frac{\%D}{\%p} = \frac{p\Delta D}{D\Delta p} = -\frac{d'(p)p}{D}.


\displaystyle d'(p^\star)p^\star + d(p^\star) = d'(p^\star)c,
\displaystyle \frac{d'(p^\star)p^\star}{d(p^\star)} + 1 = \frac{d'(p^\star)c}{d(p^\star)},
\displaystyle -\epsilon + 1 = \epsilon \frac{c}{p^\star},
\displaystyle p^\star = \frac{\epsilon c}{1-\epsilon} = \frac{c}{1-1/\epsilon}.

Moreover, knowing that \frac{\%D}{\%p} \sim \frac{\Delta \ln D}{\Delta \ln p} and using the constant elasticity model, we have that:

\displaystyle \epsilon \sim \lim_{\Delta \rightarrow0} \frac{\Delta \ln D}{\Delta \ln P} = \frac{d\ln D}{d\ln p} = \alpha.

Thus, we can calculate the optimum profit price for the constant elasticity model as:

\displaystyle p^\star = \frac{c}{1 - \frac{1}{|\alpha|}}

It is interesting to note that one needs |\alpha| > 1, otherwise the profit function will be convex with respect to price and the optimal price will be \infty. If one have a monopolistic market, normally this assumption holds.


# Synthetic data
p = seq(80,130)
d = constant_elast(p, alpha = -3, beta = 15)*exp(rnorm(sd = .15, length(p)))
c = 75
profit = d*(p-c)

# Fitting of demand model
model2 = lm(log(d)~log(p))
profit.fitted = exp(model2$fitted.values)*(p - c)

# pricing optimization
alpha = model2$coefficients[2]
p.max.profit = c/(1-1/abs(alpha))

# Plots
df.const_elast = data.frame('Prices' = p, 'Demand' = d,
                       'Profit.fitted' = profit.fitted, 'Profit' = profit)

ggplot(select(df.const_elast, Prices, Demand)) + aes(x = log(Prices), y = log(Demand)) +
  geom_point() + geom_smooth(method = lm)

plot of chunk profit_constant_elastc

ggplot(select(df.const_elast, Prices, Profit)) + aes(x = Prices, y = Profit) +
  geom_point() + geom_vline(xintercept = p.max.profit, lty = 2) +
  geom_line(data = df.const_elast, aes(x = Prices, y = Profit.fitted), color = 'blue')

plot of chunk profit_constant_elastc

Logistic model

For the logistic function, one can check that d'(p) = \alpha d(p)(1-d(p)/C). Thus:

\displaystyle d'(p^\star)(p^\star - c) + d(p^\star) = 0,
\displaystyle \alpha d(p^\star)(1-d(p^\star)/C)(p^\star-c) + d(p^\star) = 0,
\displaystyle \alpha(1-d(p^\star)/C)(p^\star-c) + 1 = 0,
\displaystyle \frac{\alpha e^{-\alpha(p^\star - p_0)}(p^\star - c) + 1+ e^{-\alpha(p^\star - p_0)}}{1+ e^{-\alpha(p^\star - p_0)}} = 0,
\displaystyle \alpha(p^\star-c)+1]e^{-\alpha(p^\star - p_0)} + 1 = 0.

Since the last equation above does not have an analytical solution (at least we couldn’t solve it), one can easily find the result with a newton-step algorithm or minimization problem. We will use the second approach with the following formulation:

\displaystyle \min_{p \in \mathbb{R}} \big{(}[\alpha(p-c)+1]e^{-\alpha(p - p_0)} + 1\big{)}^2


# Objective functions for optimization
demand_objective = function(par, p, d) sum((d - logistic(p, par[1], par[2], par[3]))^2)
price_objective = function(p, alpha, c, p0) (exp(-alpha*(p-p0))*(alpha*(p-c)+1) + 1)^2 

# A cleaner alternative for pricing optimization is to min:
price_objective2 = function(p, c, alpha, C, p0) -logistic(p, C, alpha, p0)*(p-c)

# synthetic data
p = seq(80,130)
c = 75
d = logistic(p, 120, -.15, 115) + rnorm(sd = 10, length(p))
profit = d*(p-c)

# Demand fitting, we can't use lm anymore
par.start = c(max(d), 0, mean(d)) # initial guess

demand_fit = optim(par = par.start, fn = demand_objective, method = 'BFGS',
                   p = p, d = d)

par = demand_fit$par # estimated parameters for demand function
demand.fitted = logistic(p, c = par[1], alpha = par[2], p0 = par[3])
profit.fitted = demand.fitted*(p - c)

# Pricing Optimization, we don't have a closed expression anymore
price_fit = optim(mean(p), price_objective, method = 'BFGS',
                  alpha = par[2], c = c, p0 = par[3])

# or

price_fit2 = optim(mean(p), price_objective2, method = 'BFGS',
                  c = c, C = par[1], alpha = par[2], p0 = par[3]) 

# both results are almost identical
p.max.profit = price_fit$par

# Graphics
df.logistic = data.frame('Prices' = p, 'Demand' = d, 'Demand.fitted' = demand.fitted,
                       'Profit.fitted' = profit.fitted, 'Profit' = profit)

ggplot(select(df.logistic, Prices, Demand)) + aes(x = Prices, y = Demand) +
  geom_point() +
  geom_line(data = df.logistic, aes(x = Prices, y = Demand.fitted), color = 'blue')

plot of chunk profit_logistic_demand

ggplot(select(df.logistic, Prices, Profit)) + aes(x = Prices, y = Profit) +
  geom_point() + geom_vline(xintercept = p.max.profit, lty = 2) +
  geom_line(data = df.logistic, aes(x = Prices, y = Profit.fitted), color = 'blue')

plot of chunk profit_logistic_demand

I hope you liked the examples. In the next post we will discuss about choice models, which are demand models when products are heterogeneous. Goodbye and good luck!


Since the optimal prices is a non-linear transformation of the demand parameters, the methodology above could lead to sub-optimal prices due to Jensen’s inequality. We are going to address this problem in another post with examples of how to estimate optimal prices when we have a lot of uncertainty in the demand function parameters.


Phillips, Robert Lewis. Pricing and revenue optimization. Stanford University Press, 2005.


This entry was posted in R and tagged , , , , , . Bookmark the permalink.

12 Responses to Different demand functions and optimal price estimation in R

  1. Pingback: Different demand functions and optimal price estimation in R – Mubashir Qasim

  2. dg says:

    Hi Yuri,
    nice post on optimal price estimation. I’ve two quick remarks:
    1) Aravindakshan/Ratchford (2011) show how to analytically solve for optimal prices and shares in logit-type models (https://doi.org/10.2202/1546-5616.1120).
    2) You plug point estimates of the parameters in the equations for p*. However, because of the uncertainty in the estimates and the fact that p* is a nonlinear transformation of the parameters, this can lead to sub-optimal results. A simple solution would be a parametric bootstrap.


    Liked by 1 person

    • Yuri says:

      Hi Daniel,
      Thank you for the comment! Really nice paper, I am going to take a look. About the second remark, you are absolutely correct. Jensen’s inequality gives us sub-optimal resuts.

      I did not address the problem this time otherwise the post could get too big. Definitely we are going to write a post about this issue e show some alternatives.


      Liked by 1 person

    • insightr says:

      In fact I will add this remark in the end of the post just like in the first one. Thank’s again. Best

      Liked by 1 person

  3. Pingback: Distilled News | Data Analytics & R

  4. Nad says:

    Can you kindly clarify why the demand_objective = function(par, p, d) is calculated as “sum((d – logistic(p, par[1], par[2], par[3]))^2)”, what’s the rationale behind this? I am trying to understand what the demand objective corresponds to in natural language. Thank you.


    • Nad says:

      I should have probably added, are you simply trying to minimise the squared error between a hypothetical demand function “d” and the logistic function that (best) fits d?


      • Yuri says:

        Exactly Nad, you are correct. Sorry if it wasn’t clear.

        In fact, I should mention that the “benchmark” is not necessary a hypothetical demand function “d”. It is just demand observations. In this case, we are just guessing that these observations came from a logistic function and we are trying to find the best parameters to minimize the squared error.

        Another alternative is to use the closed formula from the paper that Daniel suggested in the comment above.



  5. Cristofer Tadrissi González says:

    Hi Yuri,

    About C the scale factor, is there any method to calculate or estimate the right value?


  6. Caleb Picker says:

    Thanks for this great post. I was wondering if you were still planning to release a blog post on choice models with heterogeneous products and associated costs. Thanks!


  7. LEMUEL ROMÃO says:

    I dont understand the way that you simplify the logistic demand function. The inlfection point shoud be -B/a? How you put the p0 in demand equation?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s