Coding: BLP (1995)

Last updated on Oct 29, 2021

Intro

In this session, I am going to cover demand estimation.

  • Compute equilibrium outcomes with RCL demand
  • Simulate market-level data
    • Extremely similar to the logit demand simulation
  • Build the BLP estimator from Berry, Levinsohn, and Pakes (1995)

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_{it}$: has dimension $K$ $$\beta_{it}^k = \beta_0^k + \sigma_k \zeta_{it}^k$$
    • $\beta_0^k$: fixed taste for characteristic $k$ (the usual $\beta$)
    • $\zeta_{it}^k$: random taste, i.i.d. across consumers and markets $t$

Setup

We have $J$ firms and each product has $K$ characteristics.

i = 100;                # Number of consumers
J = 10;                 # Number of firms
K = 2;                  # Product characteristics
T = 100;                # Number of markets
β = [.5, 2, -1];        # Preferences
varζ = 5;               # Variance of the random taste
rangeJ = [2, 6];        # Min and max firms per market
varX = 1;               # Variance of X
varξ = 2;               # Variance of xi

Demand

Demand is the main difference w.r.t. the logit model. Now we have individual shocks $\zeta$ we have to integrate over.

function demand(p::Vector, X::Matrix, β::Vector, ξ::Matrix, ζ::Matrix)::Tuple{Vector, Number}
    """Compute demand"""
    δ = [X p] * (β .+ ζ)                    # Mean value
    δ0 = zeros(1, size(ζ, 2))               # Mean value of the outside option
    u = [δ; δ0] + ξ                         # Utility
    e = exp.(u)                             # Take exponential
    q = mean(e ./ sum(e, dims=1), dims=2)   # Compute demand
    return q[1:end-1], q[end]
end;

Supply

Computing profits is instead exactly the same as before. We just have to save the shocks $\zeta$ to be sure demand is stable.

function profits(p::Vector, c::Vector, X::Matrix, β::Vector, ξ::Matrix, ζ::Matrix)::Vector
    """Compute profits"""
    q, _ = demand(p, X, β, ξ, ζ)            # Compute demand
    pr = (p - c) .* q                       # Compute profits
    return pr
end;
function profits_j(pj::Number, j::Int, p::Vector, c::Vector, X::Matrix, β::Vector, ξ::Matrix, ζ::Matrix)::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;

Equilibrium

We can now compute the equilibrium for a specific market, as before.

function equilibrium(c::Vector, X::Matrix, β::Vector, ξ::Matrix, ζ::Matrix)::Vector
    """Compute equilibrium prices and profits"""
    p = 2 .* c;
    dist = 1;
    iter = 0;

    # Iterate until convergence
    while (dist > 1e-8) && (iter<1000)

        # Compute best reply for each firm
        p_old = 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());
        end

        # Update distance
        dist = max(abs.(p - p_old)...);
        iter += 1;
    end
    return p
end;

Simulating Data

We are now ready to simulate the data, i.e. equilibrium outcomes across different markets. We first draw all the variables.

function draw_data(I::Int, J::Int, K::Int, rangeJ::Vector, varζ::Number, 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, I)         # Product-level utility shocks
    # Consumer-product-level preference shocks
    ζ_ = [rand(Normal(0,1), 1, I) * varζ; zeros(K,I)]
    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;

Simulating Data

Then we simulate the data for one market.

function compute_mkt_eq(I::Int, J::Int, β::Vector, rangeJ::Vector, varζ::Number, varX::Number, varξ::Number)::DataFrame
    """Compute equilibrium one market"""

    # Initialize variables
    K = size(β, 1) - 1
    X_, ξ_, ζ_, w_, c_, j_ = draw_data(I, J, K, rangeJ, varζ, 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;

Simultate the Data (2)

We repeat for $T$ markets.

function simulate_data(I::Int, J::Int, β::Vector, T::Int, rangeJ::Vector, varζ::Number, varX::Number, varξ::Number)
    """Simulate full dataset"""
    df = compute_mkt_eq(I, J, β, rangeJ, varζ, varX, varξ)
    df[!, "t"] = ones(nrow(df)) * 1
    for t=2:T
        df_temp = compute_mkt_eq(I, J, β, rangeJ, varζ, varX, varξ)
        df_temp[!, "t"] = ones(nrow(df_temp)) * t
        append!(df, df_temp)
    end
    CSV.write("../data/blp.csv", df)
    return df
end;

Simulate the Data (3)

Now let’s run the code

# Simulate
df = simulate_data(i, J, β, T, rangeJ, varζ, varX, varξ);

The Data

What does the data look like? Let’s switch to R!

# Read data
df = fread("../data/blp.csv")
kable(df[1:6,], digits=4)
j w p q q0 pr x1 z1 x2 z2 t
4 0.6481 3.5918 0.2929 0.4558 0.8165 0.6531 1.0002 1.7063 1.8207 1
7 0.9997 5.1926 0.2513 0.4558 0.8717 1.0002 0.6531 1.8207 1.7063 1
1 0.5842 2.6999 0.0800 0.3919 0.1572 0.3591 7.1958 0.4217 2.1996 2
4 0.5291 4.5934 0.1404 0.3919 0.4467 2.3801 5.1748 0.1283 2.4929 2
5 0.5012 4.4196 0.1368 0.3919 0.4461 2.2638 5.2911 0.4408 2.1804 2
8 0.9359 3.2923 0.0863 0.3919 0.1477 0.5182 7.0367 1.0271 1.5942 2

Estimation

The BLP estimation procedure

From deltas to shares

First, we need to compute the shares implied by aspecific vector of $\delta$s

function implied_shares(Xt_::Matrix, ζt_::Matrix, δt_::Vector, δ0::Matrix)::Vector
    """Compute shares implied by deltas and shocks"""
    u = [δt_ .+ (Xt_ * ζt_); δ0]                  # Utility
    e = exp.(u)                                 # Take exponential
    q = mean(e ./ sum(e, dims=1), dims=2)       # Compute demand
    return q[1:end-1]
end;

Inner Loop

We can now compute the inner loop and invert the demand function: from shares $q$ to $\delta$s

function inner_loop(qt_::Vector, Xt_::Matrix, ζt_::Matrix)::Vector
    """Solve the inner loop: compute delta, given the shares"""
    δt_ = ones(size(qt_))
    δ0 = zeros(1, size(ζt_, 2))
    dist = 1

    # Iterate until convergence
    while (dist > 1e-8)
        q = implied_shares(Xt_, ζt_, δt_, δ0)
        δt2_ = δt_ + log.(qt_) - log.(q)
        dist = max(abs.(δt2_ - δt_)...)
        δt_ = δt2_
    end
    return δt_
end;

Compute Delta

We can now repeat the inversion for every market and get the vector of mean utilities $\delta$s from the observed market shares $q$.

function compute_delta(q_::Vector, X_::Matrix, ζ_::Matrix, T::Vector)::Vector
    """Compute residuals"""
    δ_ = zeros(size(T))

    # Loop over each market
    for t in unique(T)
        qt_ = q_[T.==t]                             # Quantity in market t
        Xt_ = X_[T.==t,:]                           # Characteristics in mkt t
        δ_[T.==t] = inner_loop(qt_, Xt_, ζ_)        # Solve inner loop
    end
    return δ_
end;

Compute Xi

Now that we have $\delta$, it is pretty straightforward to compute $\xi$. We just need to perform a linear regression (with instruments) of mean utilities $\delta$ on prices $p$ and product characteristics $X$ and compute the residuals $\xi$.

function compute_xi(X_::Matrix, IV_::Matrix, δ_::Vector)::Tuple
    """Compute residual, given delta (IV)"""
    β_ = inv(IV_' * X_) * (IV_' * δ_)           # Compute coefficients (IV)
    ξ_ = δ_ - X_ * β_                           # Compute errors
    return ξ_, β_
end;

Objective Function

We now have all the ingredients to set up the GMM objective function.

function GMM(varζ_::Number)::Tuple
    """Compute GMM objective function"""
    δ_ = compute_delta(q_, X_, ζ_ * varζ_, T)   # Compute deltas
    ξ_, β_ = compute_xi(X_, IV_, δ_)            # Compute residuals
    gmm = ξ_' * Z_ * Z_' * ξ_ / length(ξ_)^2    # Compute ortogonality condition
    return gmm, β_
end;

Estimation (1)

First, we need to set up our objects

# Retrieve data
T = Int.(df.t)
X_ = [df.x1 df.x2 df.p]
q_ = df.q
q0_ = df.q0
IV_ = [df.x1 df.x2 df.w]
Z_ = [df.x1 df.x2 df.z1 df.z2]

Estimation (2)

What would a logit regression estimate?

# Compute logit estimate
y = log.(df.q) - log.(df.q0);
β_logit = inv(IV_' * X_) * (IV_' * y);
print("Estimated logit coefficients: $β_logit")
## Estimated logit coefficients: [2.063144844221613, 1.2888511782561123, -0.9824308271558686]

Estimation (3)

We can now run the BLP machinery

# Draw shocks (less)
ζ_ = [rand(Normal(0,1), 1, i); zeros(K, i)];

# Minimize GMM objective function
varζ_ = optimize(x -> GMM(x[1])[1], [2.0], LBFGS()).minimizer[1];
β_blp = GMM(varζ_)[2];
print("Estimated BLP coefficients: $β_blp")
## Estimated BLP coefficients: [0.549234645269979, 1.1243451127088748, -0.6229637255651461]

Appendix

References [references]

Berry, Steven, James Levinsohn, and Ariel Pakes. 1995. “Automobile Prices in Market Equilibrium.” Econometrica: Journal of the Econometric Society, 841–90.

Previous
Next