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
2021-11-10
In this session, I am going to cover demand estimation.
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
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)
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.5491221990381039, 0.24359787592994134, 0.1810840379517347], 0.026195887080220043)
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.16600481498889777 ## 0.0889567306013599 ## 0.12280474674253772
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.706820256123432
What are the implied profits now?
print("Profits old: ", round.(profits(p, c, X, β, ξ), digits=4))
## Profits old: [0.166, 0.089, 0.1228]
p_new = copy(p); p_new[j] = pj; print("Profits new: ", round.(profits(p_new, c, X, β, ξ), digits=4))
## Profits new: [0.4045, 0.1405, 0.1939]
Indeed firm 1 has increased its profits.
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;
Let’s test it
# Compute equilibrium prices p_eq = equilibrium(c, X, β, ξ); print("Equilibrium prices: ", round.(p_eq, digits=4))
## Equilibrium prices: [1.9722, 1.7173, 2.0356]
# And profits pi_eq = profits(p_eq, c, X, β, ξ); print("Equilibrium profits: ", round.(pi_eq, digits=4))
## Equilibrium profits: [0.6699, 0.3521, 0.3574]
As expected the prices of the first 2 firms are lower and their profits are higher.
Let’s generate our Data Generating Process (DGP).
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;
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;
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;
We generate the dataset by simulating many markets that differ by
# 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ξ);
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 |
---|---|---|---|---|---|---|---|---|---|---|
2 | 0.7551 | 1.9353 | 0.0201 | 0.0219 | 0.0205 | 0.3786 | 2.9567 | 0.7150 | 1.1575 | 1 |
3 | 0.4460 | 4.3041 | 0.7404 | 0.0219 | 2.8514 | 2.1026 | 1.2326 | 0.4512 | 1.4212 | 1 |
6 | 0.9402 | 2.5264 | 0.1311 | 0.0219 | 0.1509 | 0.7970 | 2.5383 | 0.5200 | 1.3525 | 1 |
10 | 0.4002 | 1.8530 | 0.0865 | 0.0219 | 0.0946 | 0.0571 | 3.2782 | 0.1863 | 1.6862 | 1 |
4 | 0.5314 | 1.7842 | 0.0034 | 0.5238 | 0.0034 | 0.8688 | 1.4175 | 0.0895 | 1.4332 | 2 |
8 | 0.2636 | 2.8325 | 0.4728 | 0.5238 | 0.8969 | 1.4175 | 0.8688 | 1.4332 | 0.0895 | 2 |
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.5203 | 0.1578 | -9.6327 | 0 |
x1 | 0.3382 | 0.0569 | 5.9456 | 0 |
x2 | 1.1879 | 0.0651 | 18.2468 | 0 |
p | 0.3826 | 0.0687 | 5.5732 | 0 |
The estimate of \(\alpha = 1\) is biased (positive and significant) since \(p\) is endogenous. We need instruments.
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.7224 | 0.5213 | 1.3858 | 0.1660 |
x1 | 0.5022 | 0.0710 | 7.0704 | 0.0000 |
x2 | 1.8395 | 0.1594 | 11.5388 | 0.0000 |
p | -0.8400 | 0.2787 | -3.0145 | 0.0026 |
Now the estimate of \(\alpha\) is negative and significant.
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.1162 | 0.4419 | 2.5261 | 0.0116 |
x1 | 0.5309 | 0.0694 | 7.6457 | 0.0000 |
x2 | 1.9539 | 0.1381 | 14.1440 | 0.0000 |
p | -1.0547 | 0.2340 | -4.5075 | 0.0000 |
Also the BLP instruments deliver an estimate of \(\alpha\) is negative and significant.