3. Optimizing Marginal Revenue from the Demand Curve
The demand curve is usually defined as a plot of demand/quantity (y-axis), \(Q\), against the price (x-axis), \(P\). For nearly all products, as \(P\) goes up then \(Q\) comes down. We can model the demand curve (linearly or non-linearly) to figure out how \(P\) and \(Q\) are related, and use this relationship to find the “optimal” price that will maximize marginal revenue. Let’s see how computing the optimal price from a demand curve works.
3.1. Simulated data
Let’s simulate some data for \(P\) and \(Q\).
\(P \sim \mathcal{N}(40, 20)\)
\(Q \sim \mathcal{N}(400 - 5.0 P, 1)\)
[1]:
import pandas as pd
import numpy as np
np.random.seed(37)
N = 100
P = np.random.normal(40, 20, N) # np.arange(0, 81, 1)
Q = np.random.normal(400 + (-5 * P), 1, P.shape[0]) + np.random.normal(10, 20, N)
df = pd.DataFrame({'p': P, 'q': Q}) \
.sort_values(['p']) \
.query('p >= 0') \
.reset_index(drop=True)
df.shape
[1]:
(97, 2)
As you can see below, the demand curve is not smooth and often times, this seesaw curve is the empirical demand curve of a lot of products. We need to smooth it out.
[2]:
import matplotlib.pyplot as plt
f, a = plt.subplots(figsize=(7, 3))
df.set_index(['p'])['q'] \
.plot(
kind='line',
ax=a,
title='Demand curve',
ylabel='q'
)
f.tight_layout()
3.2. Modeling
We can model the demand curve with linear regression. The linear regression model of the demand curve is a straight line (it smooths out the empirical demand curve).
\(Q \sim P\)
[3]:
from sklearn.linear_model import LinearRegression
X = df[['p']]
y = df['q']
qp_model = LinearRegression()
qp_model.fit(X, y)
qp_model.intercept_, qp_model.coef_
[3]:
(402.0611487648074, array([-4.83565712]))
[4]:
pred_df = df.assign(q_pred=qp_model.predict(X))
f, a = plt.subplots(figsize=(7, 3))
pred_df \
.rename(columns={'q': 'q_true'}) \
.set_index(['p']) \
.plot(
kind='line',
ax=a,
title='Empirical and modeled demand curves',
ylabel='q'
)
f.tight_layout()
3.3. Marginal revenue optimization
From the linear regression model of the demand curve, we have the following relationships from the model.
\(Q = 401.96 - 4.8 P\)
\(P = 83.74 - 0.21 Q\)
We know the following about total revenue, \(R\).
\(R = P Q\)
\(R = \left(83.74 - 0.21 Q \right) Q\)
\(R = 83.74Q - 0.21 Q^2\)
The marginal revenue is \(R' = \dfrac{\mathrm{d} R}{\mathrm{d} Q}\).
\(R' = \dfrac{\mathrm{d} R}{\mathrm{d} Q} = 83.74 - 0.42 Q\)
The marginal cost, \(C'\), of producing a unit is (given as) 15.0. The optimal price can be found when we set \(R' = C'\) and solve for \(Q\) and plug back in \(Q\) to get \(P\).
\(R' = C'\)
\(83.74 - 0.42 Q = 15.0\)
\(-0.42 Q = -68.74\)
\(Q = 163.67\)
Since we know \(P = 83.74 - 0.21 Q\), we plug \(Q\) in to get the price.
\(P = 83.74 - 0.21 Q\)
\(P = 83.74 - 0.21 (163.67)\)
\(P = 49.37\)
Let’s compute \(R = PQ\).
\(R = PQ\)
\(R = 49.37 \times 163.67\)
\(R = 8,080.39\)
Let’s compute \(R'\).
\(R' = 83.74 - 0.42 Q\)
\(R' = 83.74 - 0.42 (163.67)\)
\(R' = 15.00\)
Let’s define the profit, \(T\), as follows, and plug in the values.
\(T = PQ - C'Q\)
\(T = (49.37 \times 163.67) - (15 \times 163.67)\)
\(T = 5,625.34\)
[5]:
def get_pq(b_0, b_1):
return lambda q: (-b_0 / b_1) + (1 / b_1) * q
def get_qp(b_0, b_1):
return lambda p: b_0 + (b_1 * p)
def get_mr(b_0, b_1):
return lambda q: (-b_0 / b_1) + (2 * (1 / b_1) * q)
def get_qo(b_0, b_1):
z_0 = (-b_0 / b_1)
z_1 = 2 * (1 / b_1)
return lambda mc: (mc - z_0) / z_1
def get_funcs(b_0, b_1):
pq = get_pq(b_0, b_1)
qp = get_qp(b_0, b_1)
mr = get_mr(b_0, b_1)
qo = get_qo(b_0, b_1)
r = lambda p, q: p * q
t = lambda p, q, mc: (p * q) - (mc * q)
return pq, qp, mr, qo, r, t
def get_opt(f, mc):
pq_f, qp_f, mr_f, qo_f, r_f, t_f = f
q_opt = qo_f(mc)
p_opt = pq_f(q_opt)
mr_opt = mr_f(q_opt)
r_opt = r_f(p_opt, q_opt)
t_opt = t_f(p_opt, q_opt, mc)
return pd.Series({
'mc': mc,
'q_opt': q_opt,
'p_opt': p_opt,
'mr_opt': mr_opt,
'r_opt': r_opt,
't_opt': t_opt
})
f = get_funcs(qp_model.intercept_, qp_model.coef_[0])
For this product, the optimal price is 49.08 which will lead to
164.72 units sold (quantity),
8,084.68 USD in revenue, and
5,613,77 USD in profit.
[6]:
get_opt(f, 15)
[6]:
mc 15.000000
q_opt 164.763146
p_opt 49.072545
mr_opt 15.000000
r_opt 8085.346941
t_opt 5613.899751
dtype: float64
The plot below shows the marginal revenue mr
, total revenue tr
and profit pr
at each price point p
. The vertical red dotted line marks the optimal price and it intersects with the profit curve an its highest point.
[7]:
fig, ax = plt.subplots(figsize=(7, 3))
pred_df \
.assign(
mr=lambda d: f[2](d['q_pred']),
tr=lambda d: f[4](d['p'], d['q_pred']),
pr=lambda d: f[5](d['p'], d['q_pred'], 15)
) \
.set_index(['p'])[['mr', 'tr', 'pr']] \
.plot(kind='line', ylabel='USD', ax=ax)
ax.axvline(x=get_opt(f, 15).p_opt, color='r', alpha=0.5, linestyle='dotted')
ax.legend(bbox_to_anchor=(1.1, 1.05))
fig.tight_layout()