Coding: Logit Demand
Last updated on Nov 10, 2021
Intro
In this session, I am going to cover demand estimation.
- Compute equilibrium outcomes with Logit demand
- Simulate a dataset
- Estimate Logit demand
- Compare different instruments
- Include supply
Model
In this first part, we are going to assume that consumer $i \in \lbrace1,…,I\rbrace$ utility from good $j \in \lbrace1,…,J\rbrace$ in market $t \in \lbrace1,…,T\rbrace$ takes the form
$$ u_{ijt} = \boldsymbol x_{jt} \boldsymbol \beta_{it} - \alpha p_{jt} + \xi_{jt} + \epsilon_{ijt} $$
where
- $\xi_{jt}$ is type-1 extreme value distributed
- $\boldsymbol \beta$ has dimension $K$
- i.e. goods have $K$ characteristics
Setup
We have $J$ firms and each product has $K$ characteristics
J = 3; # 3 firms == products
K = 2; # 2 product characteristics
c = rand(Uniform(0, 1), J); # Random uniform marginal costs
ξ = rand(Normal(0, 1), J+1); # Random normal individual shocks
X = rand(Exponential(1), J, K); # Random exponential product characteristics
β = [.5, 2, -1]; # Preferences (last one is for prices, i.e. alpha)
Code Demand
function demand(p::Vector, X::Matrix, β::Vector, ξ::Vector)::Tuple{Vector, Number}
"""Compute demand"""
δ = 1 .+ [X p] * β # Mean value
u = [δ; 0] + ξ # Utility
e = exp.(u) # Take exponential
q = e ./ sum(e) # Compute demand
return q[1:end-1], q[end]
end;
We can try with an example.
p = 2 .* c;
demand(p, X, β, ξ)
## ([0.4120077746005573, 0.26650568009936, 0.24027826270165709], 0.08120828259842561)
Code Supply
function profits(p::Vector, c::Vector, X::Matrix, β::Vector, ξ::Vector)::Vector
"""Compute profits"""
q, _ = demand(p, X, β, ξ) # Compute demand
pr = (p - c) .* q # Compute profits
return pr
end;
We can try with an example.
profits(p, c, X, β, ξ)
## 3-element Array{Float64,1}:
## 0.20289186252172428
## 0.1596422305025479
## 0.1270874470740512
Code Best Reply
We first code the best reply of firm $j$
function profits_j(pj::Number, j::Int, p::Vector, c::Vector, X::Matrix, β::Vector, ξ::Vector)::Number
"""Compute profits of firm j"""
p[j] = pj # Insert price of firm j
pr = profits(p, c, X, β, ξ) # Compute profits
return pr[j]
end;
Let’s test it.
j = 1;
obj_fun(pj) = - profits_j(pj[1], j, copy(p), c, X, β, ξ);
pj = optimize(x -> obj_fun(x), [1.0], LBFGS()).minimizer[1]
## 1.8019637881982011
What are the implied profits now?
print("Profits old: ", round.(profits(p, c, X, β, ξ), digits=4))
## Profits old: [0.2029, 0.1596, 0.1271]
p_new = copy(p);
p_new[j] = pj;
print("Profits new: ", round.(profits(p_new, c, X, β, ξ), digits=4))
## Profits new: [0.3095, 0.2073, 0.1651]
Indeed firm 1 has increased its profits.
Code Equilibrium
We can now compute equilibrium prices
function equilibrium(c::Vector, X::Matrix, β::Vector, ξ::Vector)::Vector
"""Compute equilibrium prices and profits"""
p = 2 .* c;
dist = 1;
iter = 0;
# Until convergence
while (dist > 1e-8) && (iter<1000)
# Compute best reply for each firm
p1 = copy(p);
for j=1:length(p)
obj_fun(pj) = - profits_j(pj[1], j, p, c, X, β, ξ);
optimize(x -> obj_fun(x), [1.0], LBFGS()).minimizer[1];
end
# Update distance
dist = max(abs.(p - p1)...);
iter += 1;
end
return p
end;
Code Equilibrium
Let’s test it
# Compute equilibrium prices
p_eq = equilibrium(c, X, β, ξ);
print("Equilibrium prices: ", round.(p_eq, digits=4))
## Equilibrium prices: [1.9764, 1.9602, 1.8366]
# And profits
pi_eq = profits(p_eq, c, X, β, ξ);
print("Equilibrium profits: ", round.(pi_eq, digits=4))
## Equilibrium profits: [0.484, 0.3612, 0.3077]
As expected the prices of the first 2 firms are lower and their profits are higher.
DGP
Let’s generate our Data Generating Process (DGP).
- $\boldsymbol x \sim exp(V_{x})$
- $\xi \sim N(0, V_{\xi})$
- $w \sim N(0, 1)$
- $\omega \sim N(0, 1)$
function draw_data(J::Int, K::Int, rangeJ::Vector, varX::Number, varξ::Number)::Tuple
"""Draw data for one market"""
J_ = rand(rangeJ[1]:rangeJ[2]) # Number of firms (products)
X_ = rand(Exponential(varX), J_, K) # Product characteristics
ξ_ = rand(Normal(0, varξ), J_+1) # Product-level utility shocks
w_ = rand(Uniform(0, 1), J_) # Cost shifters
ω_ = rand(Uniform(0, 1), J_) # Cost shocks
c_ = w_ + ω_ # Cost
j_ = sort(sample(1:J, J_, replace=false)) # Subset of firms
return X_, ξ_, w_, c_, j_
end;
Equilibrium
We first compute the equilibrium in one market.
function compute_mkt_eq(J::Int, b::Vector, rangeJ::Vector, varX::Number, varξ::Number)::DataFrame
"""Compute equilibrium one market"""
# Initialize variables
K = size(β, 1) - 1
X_, ξ_, w_, c_, j_ = draw_data(J, K, rangeJ, varX, varξ)
# Compute equilibrium
p_ = equilibrium(c_, X_, β, ξ_) # Equilibrium prices
q_, q0 = demand(p_, X_, β, ξ_) # Demand with shocks
pr_ = (p_ - c_) .* q_ # Profits
# Save to data
q0_ = ones(length(j_)) .* q0
df = DataFrame(j=j_, w=w_, p=p_, q=q_, q0=q0_, pr=pr_)
for k=1:K
df[!,"x$k"] = X_[:,k]
df[!,"z$k"] = sum(X_[:,k]) .- X_[:,k]
end
return df
end;
Simulate Dataset
We can now write the code to simulate the whole dataset.
function simulate_data(J::Int, b::Vector, T::Int, rangeJ::Vector, varX::Number, varξ::Number)
"""Simulate full dataset"""
df = compute_mkt_eq(J, β, rangeJ, varX, varξ)
df[!, "t"] = ones(nrow(df)) * 1
for t=2:T
df_temp = compute_mkt_eq(J, β, rangeJ, varX, varξ)
df_temp[!, "t"] = ones(nrow(df_temp)) * t
append!(df, df_temp)
end
CSV.write("../data/logit.csv", df)
end;
Simulate Dataset (2)
We generate the dataset by simulating many markets that differ by
- number of firms (and their identity)
- their marginal costs
- their product characteristics
# Set parameters
J = 10; # Number of firms
K = 2; # Product caracteristics
T = 500; # Markets
β = [.5, 2, -1]; # Preferences
rangeJ = [2, 6]; # Min and max firms per market
varX = 1; # Variance of X
varξ = 2; # Variance of xi
# Simulate
df = simulate_data(J, β, T, rangeJ, varX, varξ);
The Data
What does the data look like? Let’s switch to R!
# Read data
df = fread("../data/logit.csv")
kable(df[1:6,], digits=4)
j | w | p | q | q0 | pr | x1 | z1 | x2 | z2 | t |
---|---|---|---|---|---|---|---|---|---|---|
1 | 0.1491 | 1.9616 | 0.0932 | 0.0013 | 0.1028 | 2.4517 | 0.8219 | 1.6918 | 3.6779 | 1 |
2 | 0.8352 | 2.1112 | 0.0193 | 0.0013 | 0.0197 | 0.1328 | 3.1408 | 0.2075 | 5.1622 | 1 |
7 | 0.2749 | 2.2789 | 0.2710 | 0.0013 | 0.3717 | 0.1449 | 3.1287 | 1.1493 | 4.2205 | 1 |
10 | 0.4118 | 3.6386 | 0.6151 | 0.0013 | 1.5982 | 0.5442 | 2.7294 | 2.3212 | 3.0485 | 1 |
5 | 0.6071 | 2.1457 | 0.0886 | 0.0003 | 0.0972 | 0.9239 | 2.4182 | 3.6551 | 10.0474 | 2 |
6 | 0.1615 | 1.1626 | 0.0000 | 0.0003 | 0.0000 | 0.1763 | 3.1657 | 0.3629 | 13.3396 | 2 |
Estimation
First we need to compute the dependent variable
df$y = log(df$q) - log(df$q0)
Now we can estimate the logit model. The true values are $alpha=1$.
ols <- lm(y ~ x1 + x2 + p, data=df)
kable(tidy(ols), digits=4)
term | estimate | std.error | statistic | p.value |
---|---|---|---|---|
(Intercept) | -1.3558 | 0.1476 | -9.1874 | 0e+00 |
x1 | 0.4176 | 0.0537 | 7.7782 | 0e+00 |
x2 | 1.1494 | 0.0719 | 15.9903 | 0e+00 |
p | 0.2406 | 0.0656 | 3.6664 | 3e-04 |
The estimate of $\alpha = 1$ is biased (positive and significant) since $p$ is endogenous. We need instruments.
IV 1: Cost Shifters
First set of instruments: cost shifters.
fm_costiv <- ivreg(y ~ x1 + x2 + p | x1 + x2 + w, data=df)
kable(tidy(fm_costiv), digits=4)
term | estimate | std.error | statistic | p.value |
---|---|---|---|---|
(Intercept) | 0.2698 | 0.4923 | 0.5480 | 0.5837 |
x1 | 0.5249 | 0.0643 | 8.1679 | 0.0000 |
x2 | 1.8178 | 0.2064 | 8.8059 | 0.0000 |
p | -0.7034 | 0.2800 | -2.5123 | 0.0121 |
Now the estimate of $\alpha$ is negative and significant.
IV 2: BLP Instruments
Second set of instruments: product characteristics of other firms in the same market.
fm_blpiv <- ivreg(y ~ x1 + x2 + p | x1 + x2 + z1 + z2, data=df)
kable(tidy(fm_blpiv), digits=4)
term | estimate | std.error | statistic | p.value |
---|---|---|---|---|
(Intercept) | 1.6616 | 0.5014 | 3.3139 | 9e-04 |
x1 | 0.6167 | 0.0698 | 8.8380 | 0e+00 |
x2 | 2.3901 | 0.2110 | 11.3279 | 0e+00 |
p | -1.5117 | 0.2840 | -5.3221 | 0e+00 |
Also the BLP instruments deliver an estimate of $\alpha$ is negative and significant.