Assignment 1: Linear Demand Estimation¶
This homework assignment will give you practice working with the basics of the multinomial logit and nested logit demand models. It is closely related to Berry (1994) but is limited in terms of consumer heterogeneity. The following homework (problem set 2) will introduce simulation-based estimators to provide much richer sources of consumer heterogeneity.
The file \textbf{product_data.csv} contains information on 10 different soft drinks, for 100 different markets (or time periods). This dataset contains the following variables: product_ID (the product ID), nest (a variable indicating if the product is a diet or a regular soda), price (the product's price during period t, in USD), market_share (the market share of the product during period t), sugar (the grams of sugar contained in product $j$ at time $t$), caffeine (the milligrams of caffeine contained in product $j$ at time $t$), caffeine_extract_price (the price of caffeine extract paid by the firm manufacturing product $j$ at time $t$), corn_syrup_price (the price of high fructose corn syrup paid by the firm manufacturing product $j$ at time $t$), and $t$ (the observation's time period).
# Set working directory
cd("C:/Users/Eric/Documents/GitHub/Econ-543/PS 1/data")
# Load the dataset using the CSV.jl package
using CSV, DataFrames, StatsModels, Econometrics, GLM, NLsolve, Statistics, DataFramesMeta, Latexify, LinearAlgebra
product_data = CSV.File("product_data_2.csv") |> DataFrame
product_data[:, :market_share] = product_data[:, :quantity] ./ 100000
product_data
| Row | product_ID | nest | price | sugar | caffeine | caffeine_extract_price | corn_syrup_price | t | quantity | market_share |
|---|---|---|---|---|---|---|---|---|---|---|
| Int64 | String7 | Float64 | Float64 | Float64 | Float64 | Float64 | Int64 | Int64 | Float64 | |
| 1 | 1 | Diet | 1.99162 | 0.270372 | 3.1883 | 0.282726 | 0.228225 | 1 | 1789 | 0.01789 |
| 2 | 2 | Diet | 1.94978 | 0.351439 | 4.48567 | 0.142178 | 0.237764 | 1 | 12144 | 0.12144 |
| 3 | 3 | Diet | 3.3237 | 0.244885 | 6.80756 | 0.274463 | 0.283012 | 1 | 18225 | 0.18225 |
| 4 | 4 | Diet | 2.51957 | 0.391448 | 5.1014 | 0.230791 | 0.275084 | 1 | 12574 | 0.12574 |
| 5 | 5 | Diet | 2.22562 | 0.159597 | 4.31869 | 0.263295 | 0.190009 | 1 | 3624 | 0.03624 |
| 6 | 6 | Regular | 2.62901 | 3.74817 | 4.83158 | 0.19783 | 0.124657 | 1 | 8268 | 0.08268 |
| 7 | 7 | Regular | 4.05085 | 4.20814 | 5.41375 | 0.297332 | 0.316541 | 1 | 2908 | 0.02908 |
| 8 | 8 | Regular | 3.5037 | 4.35445 | 5.08304 | 0.275755 | 0.235712 | 1 | 4161 | 0.04161 |
| 9 | 9 | Regular | 3.34242 | 5.96753 | 3.63294 | 0.22253 | 0.219853 | 1 | 9942 | 0.09942 |
| 10 | 10 | Regular | 3.57558 | 4.79214 | 4.52663 | 0.279401 | 0.252537 | 1 | 4379 | 0.04379 |
| 11 | 1 | Diet | 2.55265 | 0.271625 | 5.13273 | 0.270963 | 0.263825 | 2 | 4852 | 0.04852 |
| 12 | 2 | Diet | 2.4308 | 0.94085 | 3.56033 | 0.268931 | 0.315047 | 2 | 8577 | 0.08577 |
| 13 | 3 | Diet | 2.91731 | 0.436604 | 5.95807 | 0.269629 | 0.276241 | 2 | 9574 | 0.09574 |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| 989 | 9 | Regular | 3.23082 | 4.44315 | 4.7004 | 0.279981 | 0.202717 | 99 | 1799 | 0.01799 |
| 990 | 10 | Regular | 3.07926 | 4.97266 | 4.72389 | 0.141377 | 0.235914 | 99 | 10565 | 0.10565 |
| 991 | 1 | Diet | 2.2333 | 0.0189666 | 4.45375 | 0.267307 | 0.241027 | 100 | 2818 | 0.02818 |
| 992 | 2 | Diet | 3.11017 | 0.622346 | 7.13833 | 0.224627 | 0.247321 | 100 | 20256 | 0.20256 |
| 993 | 3 | Diet | 4.03423 | 0.704336 | 7.49587 | 0.304058 | 0.248869 | 100 | 28435 | 0.28435 |
| 994 | 4 | Diet | 2.7324 | 0.184933 | 5.39084 | 0.301412 | 0.228848 | 100 | 4803 | 0.04803 |
| 995 | 5 | Diet | 2.69605 | 0.631173 | 5.54092 | 0.258955 | 0.279003 | 100 | 6287 | 0.06287 |
| 996 | 6 | Regular | 3.25233 | 4.77385 | 2.4499 | 0.245779 | 0.331281 | 100 | 763 | 0.00763 |
| 997 | 7 | Regular | 3.57212 | 5.05058 | 4.46191 | 0.261233 | 0.278739 | 100 | 1516 | 0.01516 |
| 998 | 8 | Regular | 3.54548 | 4.86676 | 5.28524 | 0.157817 | 0.297487 | 100 | 7363 | 0.07363 |
| 999 | 9 | Regular | 3.71408 | 3.58529 | 6.84865 | 0.228642 | 0.238139 | 100 | 7915 | 0.07915 |
| 1000 | 10 | Regular | 3.91448 | 4.25339 | 5.81522 | 0.290203 | 0.267531 | 100 | 3944 | 0.03944 |
1(a): Multinomial Logit¶
Assume the indirect utility of consumer $i$ for consuming soft drink $j$ in period $t$ is given by:
\begin{equation} u_{ijt} = \alpha p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + \xi_{jt} + \varepsilon_{ijt} \end{equation}
where $p_{jt}$ denotes the price (in USD) of product $j$ at time $t$, $sug_{jt}$ denote $j$'s sugar content (in grams) at time $t$, $caf_{jt}$ denotes $j$'s milligrams of caffeine at time $t$, $Diet_j$ is an indicator variable equal to 1 if $j$ is a diet product, $Regular_j$ is an indicator variable equal to 1 if $j$ is a regular soda, and $\xi_{jt}$ represents the unobserved quality of product $j$ during time period $t$.
Moreover, assume that $\varepsilon_{ijt}$ are $iid$ draws from a standard type 1 extreme value distribution. Note that the only source of heterogeneity between consumer preferences comes from the extreme value shock.
Finally, assume that there is an outside option (product 0) representing not consuming any soft drinks. The utility of the outside option is normalized such that it is mean zero:
\begin{equation} u_{i0t} = \varepsilon_{i0t} \end{equation}
Recall that given our distributional assumptions over $\varepsilon_{ijt}$, the probability that consumer $i$ chooses product $j$ during time $t$ is:
$$ s_{ijt} = \mathbb{P}(u_{ijt} > u_{ikt} \: \text{for all} \: k) = \frac{\exp(\delta_{jt})}{\sum_k \exp(\delta_k) + 1 } $$
With $\delta_{jt} = \alpha p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + \xi_{jt}$.
Assume that $p_{jt}$, $sug_{jt}$, and $caf_{jt}$ are all exogenous variables. Estimate the parameters of this model ($\alpha$, $\beta_1$, $\beta_2$, $\gamma_d$, and $\gamma_r$).
Solution¶
Note that we can rearrange the market share equation for the outside option (product $0$) to get the following expression:
$$ \delta_{jt} = \log(s_{jt}) - \log(s_{0t}) $$
We can then substitute this expression into equation (3) to get:
$$ \log(s_{jt}) - \log(s_{0t}) =\alpha p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + \xi_{jt} $$
Given that this is a linear equation, and we have assumed that $p_{jt}$, $sug_{jt}$, $caf_{jt}$, $Diet_j$, and $Regular_j$ are all exogenous, we can estimate the parameters of this model by OLS.
function multinomial_logit_coeff(product_data::DataFrame, estimation::String="IV")
data = copy(product_data)
# Compute outside_share
grouped_data = groupby(data, :t)
outside_share_df = combine(grouped_data, :market_share => (x -> 1 .- sum(x)) => :outside_share)
merged_data = leftjoin(product_data, outside_share_df, on=:t)
# Compute delta_jt
merged_data[:, :delta_jt] = log.(merged_data[:, :market_share]) .- log.(merged_data[:, :outside_share])
# Add dummy columns
merged_data.dummy_Diet = ifelse.(merged_data.nest .== "Diet", 1, 0)
merged_data.dummy_Regular = ifelse.(merged_data.nest .== "Regular", 1, 0)
if estimation == "IV"
coefficients = fit(EconometricModel,
@formula(delta_jt ~ 0 + sugar + caffeine + dummy_Diet + dummy_Regular + (price ~ caffeine_extract_price + corn_syrup_price)),
merged_data,
panel = :product_ID,
time = :t)
elseif estimation == "OLS"
formula = @formula(delta_jt ~ 0 + price + sugar + caffeine + dummy_Diet + dummy_Regular)
coefficients = lm(formula, merged_data)
else
error("estimation must be either IV or OLS")
end
return coefficients
end
multinomial_logit_coeff (generic function with 2 methods)
coefficients_iv = multinomial_logit_coeff(product_data, "OLS")
StatsModels.TableRegressionModel{LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}}}}, Matrix{Float64}}
delta_jt ~ 0 + price + sugar + caffeine + dummy_Diet + dummy_Regular
Coefficients:
────────────────────────────────────────────────────────────────────────────
Coef. Std. Error t Pr(>|t|) Lower 95% Upper 95%
────────────────────────────────────────────────────────────────────────────
price -0.337102 0.0512248 -6.58 <1e-10 -0.437623 -0.236581
sugar 0.708483 0.0269605 26.28 <1e-99 0.655577 0.761389
caffeine 0.691937 0.0231248 29.92 <1e-99 0.646558 0.737316
dummy_Diet -3.8965 0.0823602 -47.31 <1e-99 -4.05812 -3.73488
dummy_Regular -7.10876 0.126564 -56.17 <1e-99 -7.35712 -6.86039
────────────────────────────────────────────────────────────────────────────
Problem 1(b)¶
Now assume that $sweetness_{jt}$, and $healthiness_{jt}$ are exogenous, but that $price_{jt}$ is correlated with the unobserved quality $\xi_{jt}$. Use $Z1_{jt}$ and $Z2_{jt}$ as instruments for prices, and estimate $\alpha$, $\beta_1$, and $\beta_2$.
Solution¶
If $price_{jt}$ is correlated with the unobserved product quality $\xi_{jt}$, the estimates we obtained in part (a) will be biased. We can therefore use the usual 2SLS IV estimator using Z1 and Z2 as instruments for prices.
coefficients_iv = multinomial_logit_coeff(product_data, "IV")
Continuous Response Model
Number of observations: 1000
Null Loglikelihood: -1308.75
Loglikelihood: -690.20
R-squared: NaN
LR Test: 1237.10 ∼ χ²(5) ⟹ Pr > χ² = 0.0000
Formula: delta_jt ~ 0 + sugar + caffeine + dummy_Diet + dummy_Regular + (price ~ caffeine_extract_price + corn_syrup_price)
Variance Covariance Estimator: OIM
─────────────────────────────────────────────────────────────────────────────
PE SE t-value Pr > |t| 2.50% 97.50%
─────────────────────────────────────────────────────────────────────────────
sugar 0.860601 0.030166 28.5289 <1e-99 0.801405 0.919798
caffeine 0.850355 0.0267068 31.8404 <1e-99 0.797947 0.902763
dummy_Diet -3.60285 0.0882533 -40.824 <1e-99 -3.77603 -3.42966
dummy_Regular -7.00604 0.131703 -53.1957 <1e-99 -7.26449 -6.7476
price -0.785799 0.0626406 -12.5446 <1e-32 -0.908722 -0.662875
─────────────────────────────────────────────────────────────────────────────
Problem 1(c)¶
Use the parameters you estimated in problem 1 (b) to derive the vector of price own-price derivatives $\frac{\partial s_{jt}}{\partial p_{jt}}$ for all $j$ and all $t$. Use this vector to estimate the vector of price elasticities $\frac{\partial s_{jt}}{\partial p_{jt}} \frac{p_{jt}}{s_{jt}}$ for all $j$ and all $t$.
Solution¶
The own-price derivative of the choice probability for product $j$ in period $t$ is:
$$ \frac{\partial s_{jt}}{\partial p_{jt} }=-\alpha s_{ jt}\left(1-s_{jt}\right) $$
Therefore, the own-price elasticity for product $j$ in period $t$ is:
$$ -\alpha s_{ jt}\left(1-s_{jt}\right) \times \frac{p_{jt}}{s_{jt}} $$
function price_elasticity_mult_logit(product_data::DataFrame, alpha::Float64)
data = copy(product_data)
# Compute the price_derivative and price_elasticities
data[!, :price_derivative] = alpha .* data[!, :market_share] .* (1 .- data[!, :market_share])
data[!, :price_elasticities] = data[!, :price_derivative] .* (data[!, :price] ./ data[!, :market_share])
# Select the desired columns for the result
result = select(data, :product_ID, :nest, :price_derivative, :price_elasticities, :t)
return result
end
alpha_hat_mult = coef(coefficients_iv)[5]
own_price_elasticity_mult = price_elasticity_mult_logit(product_data, alpha_hat_mult)
| Row | product_ID | nest | price_derivative | price_elasticities | t |
|---|---|---|---|---|---|
| Int64 | String7 | Float64 | Float64 | Int64 | |
| 1 | 1 | Diet | -0.0138064 | -1.53701 | 1 |
| 2 | 2 | Diet | -0.0838387 | -1.34607 | 1 |
| 3 | 3 | Diet | -0.117111 | -2.13577 | 1 |
| 4 | 4 | Diet | -0.0863824 | -1.73093 | 1 |
| 5 | 5 | Diet | -0.0274453 | -1.68551 | 1 |
| 6 | 6 | Regular | -0.0595981 | -1.89507 | 1 |
| 7 | 7 | Regular | -0.0221865 | -3.09059 | 1 |
| 8 | 8 | Regular | -0.0313366 | -2.63864 | 1 |
| 9 | 9 | Regular | -0.070357 | -2.36534 | 1 |
| 10 | 10 | Regular | -0.0329033 | -2.68665 | 1 |
| 11 | 1 | Diet | -0.036277 | -1.90854 | 2 |
| 12 | 2 | Diet | -0.0616172 | -1.74629 | 2 |
| 13 | 3 | Diet | -0.0680296 | -2.07295 | 2 |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| 989 | 9 | Regular | -0.0138822 | -2.4931 | 99 |
| 990 | 10 | Regular | -0.0742486 | -2.16404 | 99 |
| 991 | 1 | Diet | -0.0215198 | -1.70547 | 100 |
| 992 | 2 | Diet | -0.12693 | -1.94892 | 100 |
| 993 | 3 | Diet | -0.159906 | -2.26868 | 100 |
| 994 | 4 | Diet | -0.0359292 | -2.04399 | 100 |
| 995 | 5 | Diet | -0.0462972 | -1.98536 | 100 |
| 996 | 6 | Regular | -0.0059499 | -2.53617 | 100 |
| 997 | 7 | Regular | -0.0117321 | -2.76441 | 100 |
| 998 | 8 | Regular | -0.0535982 | -2.5809 | 100 |
| 999 | 9 | Regular | -0.0572732 | -2.68752 | 100 |
| 1000 | 10 | Regular | -0.0297696 | -2.95467 | 100 |
Problem 1(d)¶
Use the parameters you estimated in problem 1 (b) to derive the vector of cross-price derivatives $\frac{\partial s_{jt}}{\partial p_{1t}}$ for all $j \neq 1$ and all $t$. Use this vector to estimate the vector of cross-price elasticities $\frac{\partial s_{jt}}{\partial p_{1t}} \frac{p_{1t}}{s_{jt}}$ for all $j \neq 1$ and all $t$.
Solution¶
The cross-price derivatives are
$$ \frac{\partial s_{jt}}{\partial p_{1t} }=\alpha s_{1t} s_{jt} $$
Therefore, the cross-price elasticity for product $j$ with respect to product $1$ in period $t$ is:
$$ \alpha s_{1t} s_{jt} \times \frac{p_{1t}}{s_{jt}} $$
function cross_price_elasticity_mult_logit(product_data::DataFrame, alpha::Float64, product)
data = copy(product_data)
# Selecting the specified product
selected_product = filter(row -> row[:product_ID] == product, data)
# Creating the selected_product DataFrame
selected_product = select(selected_product, :product_ID => :product_k_ID, :price => :price_k, :market_share => :share_k, :t)
# Merging the data with selected_product
data = leftjoin(data, selected_product, on = :t)
# Compute the cross_price_derivative and cross_price_elasticities
data[!, :cross_price_derivative] = - alpha .* data[!, :market_share] .* data[!, :share_k]
data[!, :cross_price_elasticities] = data[!, :cross_price_derivative] .* (data[!, :price_k] ./ data[!, :market_share])
# Removing rows with the specified product
filter!(row -> row[:product_ID] != product, data)
# Selecting the desired columns for the result
result = select(data, :product_ID, :nest, :cross_price_derivative, :cross_price_elasticities, :t)
return result
end
cross_price_elasticity_mult_logit_vec = cross_price_elasticity_mult_logit(product_data, alpha_hat_mult, 1)
| Row | product_ID | nest | cross_price_derivative | cross_price_elasticities | t |
|---|---|---|---|---|---|
| Int64 | String7 | Float64 | Float64 | Int64 | |
| 1 | 2 | Diet | 0.0017072 | 0.027998 | 1 |
| 2 | 3 | Diet | 0.00256206 | 0.027998 | 1 |
| 3 | 4 | Diet | 0.00176765 | 0.027998 | 1 |
| 4 | 5 | Diet | 0.00050946 | 0.027998 | 1 |
| 5 | 6 | Regular | 0.00116231 | 0.027998 | 1 |
| 6 | 7 | Regular | 0.000408805 | 0.027998 | 1 |
| 7 | 8 | Regular | 0.000584951 | 0.027998 | 1 |
| 8 | 9 | Regular | 0.00139764 | 0.027998 | 1 |
| 9 | 10 | Regular | 0.000615597 | 0.027998 | 1 |
| 10 | 2 | Diet | 0.00327015 | 0.0973246 | 2 |
| 11 | 3 | Diet | 0.00365027 | 0.0973246 | 2 |
| 12 | 4 | Diet | 0.00486347 | 0.0973246 | 2 |
| 13 | 5 | Diet | 0.00250875 | 0.0973246 | 2 |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| 889 | 8 | Regular | 0.00319097 | 0.0952597 | 99 |
| 890 | 9 | Regular | 0.000674171 | 0.0952597 | 99 |
| 891 | 10 | Regular | 0.00395921 | 0.0952597 | 99 |
| 892 | 2 | Diet | 0.00448545 | 0.0494537 | 100 |
| 893 | 3 | Diet | 0.00629659 | 0.0494537 | 100 |
| 894 | 4 | Diet | 0.00106357 | 0.0494537 | 100 |
| 895 | 5 | Diet | 0.00139218 | 0.0494537 | 100 |
| 896 | 6 | Regular | 0.000168957 | 0.0494537 | 100 |
| 897 | 7 | Regular | 0.0003357 | 0.0494537 | 100 |
| 898 | 8 | Regular | 0.00163045 | 0.0494537 | 100 |
| 899 | 9 | Regular | 0.00175268 | 0.0494537 | 100 |
| 900 | 10 | Regular | 0.000873352 | 0.0494537 | 100 |
Problem 2(e)¶
Write a function that generates the Jacobian matrix of price derivatives $\Delta(p)$ (i.e., $\Delta_{ij} = \frac{\partial s_j}{p_i}$) for a given period. Your function should take the time period, product variables, and model parameters as arguments, and return the Jacobian matrix. Print the Jacobian for the last time period in the sample (t = 100).
Solution¶
using LinearAlgebra
function build_jacobian_matrix_mult_logit(product_variables::DataFrame, alpha::Number, time_period)
dd = copy(product_variables)
dd = dd[dd.t .== time_period, :]
sort!(dd, [:nest, :product_ID])
dd[!, :self_derivative] = alpha * dd.market_share .* (1 .- dd.market_share)
Jacobian_matrix = dd.market_share * (-alpha .* dd.market_share)'
# Update the diagonal of Jacobian_matrix with self_derivative values
Jacobian_matrix[diagind(Jacobian_matrix)] = dd.self_derivative
return Jacobian_matrix
end
Jacobian_mult = build_jacobian_matrix_mult_logit(product_data, alpha_hat_mult, 100)
latexify(round.(Jacobian_mult, digits=4))
2(a): Nested Logit¶
Now suppose that there are two sources of consumer heterogeneity (1) an extreme value shock as in problem 1 and (2) a preference for diet soda. Let products $0,1,2,...,20$ be grouped into three mutually exclusive sets: $\mathcal{J}_D$ (for diet products), $\mathcal{J}_R$ (for regular products), and $\mathcal{J}_0$ (for the outside option). For product $j \in \mathcal{J}_g$ during time $t$, let consumer $i$ derive utility equal to:
\begin{equation} u_{ijt} = \alpha p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + \xi_{jt} + \varsigma_{igt} + (1 - \sigma) \times \varepsilon_{ijt} \end{equation}
Where $\varepsilon_{ijt}$ is again distributed type 1 extreme value, and $\varsigma_{igt}$ follows a distribution such that $\varsigma_{igt} + (1 - \sigma) \times \varepsilon_{ijt}$ is also an extreme value random variable. The parameter $\sigma$ represents the degree of correlation between the error terms of products in the same nest and satisfies $0 \leq \sigma < 1$.
Given our distributional assumptions, the inside share of good $j$ belonging to group $g$ at time $t$ is given by:
\begin{equation} s_{jt|g} = \frac{ \exp(\frac{\delta_{jt}}{1 - \sigma}) }{ \sum_{k \in \mathcal{J}_g } \exp(\frac{\delta_{kt}}{1 - \sigma})} \end{equation}
with $\delta_{jt} = \alpha p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + \xi_{jt}$
The probability that a product from group $g$ is chosen is
\begin{equation} s_g = \frac{D_{g}^{(1 - \sigma)}}{\sum_g D_{g}^{(1 - \sigma)}} \end{equation}
with $D_g = \sum_{k \in \mathcal{J}_g } \exp(\frac{\delta_{kt}}{1 - \sigma})$
Therefore, product $j$'s share at time $t$ is
\begin{equation} s_{jt} = s_{jt|g} \times s_g \end{equation}
Estimate the parameters of the model ($\alpha$, $\beta_1$, $\beta_2$, $\gamma_d$, and $\gamma_r$, and $\sigma$), assuming that $p_{jt}$ is correlated with the unobserved quality $\xi_{jt}$.
Solution¶
We can invert the demand system to get the following linear expression:
$$ \log \left(s_{j t}\right)- \log \left(s_{0 t}\right)= \alpha \times p_{jt} + \beta_1 sug_{jt} + \beta_2 caf_{jt} + \gamma_d Diet_j + \gamma_r Regular_j + (1-\sigma) \log \left(s_{jt| g}\right)+\xi_{j t} $$
As before, we can use the prices of caffeine and corn syrup as instruments for prices. However, note that $s_{jt| g}$ is also endogenous, so we need instruments for the inside share as well. Given our assumptions that $sug_{jt}$ and $caf_{jt}$ are exogenous, we can use the average $sug_{jt}$ and $caf_{jt}$ of competing products inside the same nest to instrument for $s_{jt| g}$, then estimate $\alpha$, $\beta_1$, $\beta_2$, and $\sigma$ by 2SLS IV.
function nested_logit_coeff(product_data::DataFrame)
data = copy(product_data)
grouped_by_t = groupby(data, :t)
outside_share_df = combine(grouped_by_t, :market_share => (x -> 1 .- sum(x)) => :outside_share)
data = leftjoin(data, outside_share_df, on=:t)
data[:, :delta_jt] = log.(data.market_share) .- log.(data.outside_share)
grouped_by_t_nest = groupby(data, [:t, :nest])
nest_share_df = combine(grouped_by_t_nest, :market_share => sum => :nest_share)
data = leftjoin(data, nest_share_df, on=[:t, :nest])
data[:, :inside_share] = data[:, :market_share] ./ data[:, :nest_share]
# Compute adjusted sum and count
sugar_sum_df = combine(grouped_by_t_nest, :sugar => (x -> sum(x) .- x) => :adj_sum, :sugar => length => :sugar_count)
sugar_sum_df[:, :competitors_sugar] = sugar_sum_df[:, :adj_sum] ./ (sugar_sum_df[:, :sugar_count] .- 1)
#Compute adjusted sum and count
caffeine_sum_df = combine(grouped_by_t_nest, :caffeine => (x -> sum(x) .- x) => :adj_sum, :caffeine => length => :caffeine_count)
caffeine_sum_df[:, :competitors_caffeine] = caffeine_sum_df[:, :adj_sum] ./ (caffeine_sum_df[:, :caffeine_count] .- 1)
sort!(data, [:t, :product_ID])
sugar_sum_df = DataFrame(product_ID = data.product_ID, competitors_sugar = sugar_sum_df.competitors_sugar, t = data.t)
caffeine_sum_df = DataFrame(product_ID = data.product_ID, competitors_caffeine = caffeine_sum_df.competitors_caffeine, t = data.t)
data = leftjoin(data, sugar_sum_df, on=[:t, :product_ID])
data = leftjoin(data, caffeine_sum_df, on=[:t, :product_ID])
# Add dummy columns
data.dummy_Diet = ifelse.(data.nest .== "Diet", 1, 0)
data.dummy_Regular = ifelse.(data.nest .== "Regular", 1, 0)
coefficients = fit(EconometricModel,
@formula(delta_jt ~0 + sugar + caffeine + dummy_Diet + dummy_Regular + (log(inside_share) + price ~ caffeine_extract_price + corn_syrup_price + competitors_sugar + competitors_caffeine)),
data)
xi = residuals(coefficients)
xi_hat = DataFrame(product_ID = data.product_ID, xi = xi, t = data.t)
return Dict(:coefficients => coefficients, :xi_hat => xi_hat)
end
nested_logit_result = nested_logit_coeff(product_data)
nested_logit_result[:coefficients]
Continuous Response Model
Number of observations: 1000
Null Loglikelihood: -1308.75
Loglikelihood: 8.14
R-squared: NaN
LR Test: 2633.78 ∼ χ²(6) ⟹ Pr > χ² = 0.0000
Formula: delta_jt ~ 0 + sugar + caffeine + dummy_Diet + dummy_Regular + (:(log(inside_share)) + price ~ caffeine_extract_price + corn_syrup_price + competitors_sugar + competitors_caffeine)
Variance Covariance Estimator: OIM
─────────────────────────────────────────────────────────────────────────────────
PE SE t-value Pr > |t| 2.50% 97.50%
─────────────────────────────────────────────────────────────────────────────────
sugar 0.494104 0.0204195 24.1977 <1e-99 0.454034 0.534175
caffeine 0.490971 0.0188912 25.9894 <1e-99 0.4539 0.528042
dummy_Diet -1.53519 0.103962 -14.7668 <1e-44 -1.7392 -1.33118
dummy_Regular -3.56714 0.165241 -21.5875 <1e-84 -3.8914 -3.24287
log(inside_share) 0.494244 0.0215998 22.8819 <1e-92 0.451858 0.536631
price -0.466178 0.0316167 -14.7447 <1e-43 -0.528221 -0.404134
─────────────────────────────────────────────────────────────────────────────────
Problem 2(b)¶
Use the parameters you estimated in problem 2 (a) to derive the vector of price self-derivatives $\frac{\partial s_{jt}}{\partial p_{jt}}$ for all $j$ and all $t$. Use this vector to estimate the vector of price elasticities $\frac{\partial s_{jt}}{\partial p_{jt}} \frac{p_{jt}}{s_{jt}}$ for all $j$ and all $t$.
Solution¶
The derivative of product $j$'s share, for $j \in \mathcal{J}_g$, with respect to its own price is given by
$$ \frac{\partial s_{jt}}{\partial p_{jt}} = \frac{\partial \delta_{jt}}{\partial p_{jt}} \times \frac{\partial s_{jt} }{\partial \delta_{jt} }=\frac{\alpha}{1-\sigma} s_{jt} \left(1-\sigma s_{jt | g}-(1-\sigma) s_{jt} \right) $$
The own price elasticity of product $j$ in group $g$ is therefore given by
$$ \frac{\alpha}{1-\sigma} s_{jt} \left(1-\sigma s_{jt | g}-(1-\sigma) s_{jt} \right) \times \frac{p_{jt}}{s_{jt}} $$
function price_elasticity_nested_logit(product_data::DataFrame, alpha, sigma)
data = copy(product_data)
# Compute nest_share
grouped_by_t_nest = groupby(data, [:t, :nest])
nest_share_df = combine(grouped_by_t_nest, :market_share => sum => :nest_share)
data = leftjoin(data, nest_share_df, on=[:t, :nest])
# Compute inside_share
data[:, :inside_share] = data[:, :market_share] ./ data[:, :nest_share]
# Compute price_derivative
data[:, :price_derivative] = (alpha / (1 - sigma)) .* data[:, :market_share] .* (1 .- sigma .* data[:, :inside_share] .- (1 .- sigma) .* data[:, :market_share])
# Compute price_elasticities
data[:, :price_elasticities] = data[:, :price_derivative] .* (data[:, :price] ./ data[:, :market_share])
# Create result DataFrame
result = select(data, :product_ID, :nest, :price_derivative, :price_elasticities, :t)
return result
end
alpha_hat_nested = coef(nested_logit_result[:coefficients])[6]
sigma_hat = coef(nested_logit_result[:coefficients])[5]
price_elasticity_nested_logit_vec = price_elasticity_nested_logit(product_data, alpha_hat_nested , sigma_hat)
| Row | product_ID | nest | price_derivative | price_elasticities | t |
|---|---|---|---|---|---|
| Int64 | String7 | Float64 | Float64 | Int64 | |
| 1 | 1 | Diet | -0.0160393 | -1.78559 | 1 |
| 2 | 2 | Diet | -0.0911677 | -1.46374 | 1 |
| 3 | 3 | Diet | -0.121212 | -2.21054 | 1 |
| 4 | 4 | Diet | -0.0936344 | -1.87624 | 1 |
| 5 | 5 | Diet | -0.0315545 | -1.93787 | 1 |
| 6 | 6 | Regular | -0.0625225 | -1.98806 | 1 |
| 7 | 7 | Regular | -0.0251111 | -3.49799 | 1 |
| 8 | 8 | Regular | -0.0348871 | -2.93761 | 1 |
| 9 | 9 | Regular | -0.071849 | -2.4155 | 1 |
| 10 | 10 | Regular | -0.0365238 | -2.98227 | 1 |
| 11 | 1 | Diet | -0.0410925 | -2.16188 | 2 |
| 12 | 2 | Diet | -0.067713 | -1.91905 | 2 |
| 13 | 3 | Diet | -0.074112 | -2.25828 | 2 |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| 989 | 9 | Regular | -0.0160619 | -2.88454 | 99 |
| 990 | 10 | Regular | -0.0794368 | -2.31525 | 99 |
| 991 | 1 | Diet | -0.0250267 | -1.98339 | 100 |
| 992 | 2 | Diet | -0.137721 | -2.11461 | 100 |
| 993 | 3 | Diet | -0.165563 | -2.34893 | 100 |
| 994 | 4 | Diet | -0.0415171 | -2.36188 | 100 |
| 995 | 5 | Diet | -0.0532309 | -2.2827 | 100 |
| 996 | 6 | Regular | -0.00688242 | -2.93367 | 100 |
| 997 | 7 | Regular | -0.0133796 | -3.15259 | 100 |
| 998 | 8 | Regular | -0.0538538 | -2.59321 | 100 |
| 999 | 9 | Regular | -0.0567618 | -2.66353 | 100 |
| 1000 | 10 | Regular | -0.0323326 | -3.20906 | 100 |
Problem 2(c)¶
Use the parameters you estimated in problem 2 (a) to derive the vector of cross-price derivatives $\frac{\partial s_{jt}}{\partial p_{1t}}$ for all $j \neq 1$ and all $t$. Use this vector to estimate the vector of cross-price elasticities $\frac{\partial s_{jt}}{\partial p_{1t}} \frac{p_{1t}}{s_{jt}}$ for all $j \neq 1$ and all $t$.
Solution¶
The derivative of product $j$ 's share with respect to product $k$ 's price, for $j$ and $k$ both in group $g$, equals
$$ \frac{\partial s_{jt}}{\partial p_{kt}} = \frac{\partial \delta_{kt}}{\partial p_{kt}} \times \frac{\partial s_{jt} }{\partial \delta_{kt} }=-\alpha s_{kt} \left(s_{jt} +\frac{\sigma}{1-\sigma} s_{jt|g}\right) $$
The cross-price derivative is therefore given by
$$ -\alpha s_{kt} \left(s_{jt} +\frac{\sigma}{1-\sigma} s_{jt|g}\right) \times \frac{p_{kt}}{s_{jt}} $$
The derivative of product $j$ 's share with respect to product $k$ 's mean valuation, for $j$ and $k$ in different groups, equals $$ \frac{\partial s_{jt}}{\partial p_{kt}} = \frac{\partial \delta_{kt}}{\partial p_{kt}} \times \frac{\partial s_{jt} }{\partial \delta_{kt} } = -\alpha s_{jt} s_{kt} $$
The cross-price elasticity is therefore given by
$$ -\alpha s_{jt} s_{kt} \times \frac{p_{kt}}{s_{jt}} $$
function cross_price_elasticity_nested_logit(product_data::DataFrame, alpha, sigma, product)
data = copy(product_data)
# Compute nest_share and inside_share
grouped_by_t_nest = groupby(data, [:t, :nest])
nest_share_df = combine(grouped_by_t_nest, :market_share => sum => :nest_share)
data = leftjoin(data, nest_share_df, on=[:t, :nest])
data[:, :inside_share] = data[:, :market_share] ./ data[:, :nest_share]
# Get the selected_product
selected_product = filter(row -> row.product_ID == product, data)
selected_product_df = DataFrame(
product_k_ID = selected_product.product_ID,
nest_k = selected_product.nest,
price_k = selected_product.price,
share_k = selected_product.market_share,
t = selected_product.t
)
# Merge the two DataFrames
data = leftjoin(data, selected_product_df, on=:t)
# Compute cross_price_derivative
data[:, :cross_price_derivative] = ifelse.(
data.nest .== data.nest_k,
- alpha .* data.share_k .* (data.market_share + (sigma / (1 - sigma)) .* data.inside_share),
- alpha .* data.market_share .* data.share_k
)
# Compute cross_price_elasticities
data[:, :cross_price_elasticities] = data[:, :cross_price_derivative] .* (data[:, :price_k] ./ data[:, :market_share])
# Filter out rows where product_ID equals the selected product
data = filter(row -> row.product_ID != product, data)
# Create result DataFrame
result = select(data, :product_ID, :nest, :cross_price_derivative, :cross_price_elasticities, :t)
return result
end
cross_price_elasticity_vec = cross_price_elasticity_nested_logit(product_data, alpha_hat_nested, sigma_hat, 1)
| Row | product_ID | nest | cross_price_derivative | cross_price_elasticities | t |
|---|---|---|---|---|---|
| Int64 | String7 | Float64 | Float64 | Int64 | |
| 1 | 2 | Diet | 0.00305959 | 0.0501774 | 1 |
| 2 | 3 | Diet | 0.00459166 | 0.0501774 | 1 |
| 3 | 4 | Diet | 0.00316793 | 0.0501774 | 1 |
| 4 | 5 | Diet | 0.000913041 | 0.0501774 | 1 |
| 5 | 6 | Regular | 0.000689544 | 0.0166099 | 1 |
| 6 | 7 | Regular | 0.000242525 | 0.0166099 | 1 |
| 7 | 8 | Regular | 0.000347024 | 0.0166099 | 1 |
| 8 | 9 | Regular | 0.000829155 | 0.0166099 | 1 |
| 9 | 10 | Regular | 0.000365205 | 0.0166099 | 1 |
| 10 | 2 | Diet | 0.00641786 | 0.191005 | 2 |
| 11 | 3 | Diet | 0.00716388 | 0.191005 | 2 |
| 12 | 4 | Diet | 0.00954486 | 0.191005 | 2 |
| 13 | 5 | Diet | 0.00492358 | 0.191005 | 2 |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| 889 | 8 | Regular | 0.00189306 | 0.0565131 | 99 |
| 890 | 9 | Regular | 0.000399954 | 0.0565131 | 99 |
| 891 | 10 | Regular | 0.00234881 | 0.0565131 | 99 |
| 892 | 2 | Diet | 0.00681513 | 0.0751393 | 100 |
| 893 | 3 | Diet | 0.00956696 | 0.0751393 | 100 |
| 894 | 4 | Diet | 0.00161597 | 0.0751393 | 100 |
| 895 | 5 | Diet | 0.00211526 | 0.0751393 | 100 |
| 896 | 6 | Regular | 0.000100234 | 0.0293386 | 100 |
| 897 | 7 | Regular | 0.000199155 | 0.0293386 | 100 |
| 898 | 8 | Regular | 0.000967269 | 0.0293386 | 100 |
| 899 | 9 | Regular | 0.00103978 | 0.0293386 | 100 |
| 900 | 10 | Regular | 0.000518119 | 0.0293386 | 100 |
Problem 2(d)¶
How do the results you obtained from parts 2 (a), 2 (b), and 2 (c) compare to the ones from parts 1 (b), 1 (c), and 1(d)?
Solution¶
Note that given the assumptions of Multinomial Logit, the cross-price elasticity of all products with respect to product 1 is constant (see problem 1(d)). To see why, let's go back to the expression for $\frac{\partial s_{jt}}{\partial p_{kt} }$. Note that this expression implies that the share of consumers who switch from product $k$ to product $j$ only depends on the market shares of products $j$ and $k$. For instance, suppose that $k$ is a diet soda. If $j$ and $l$ are a diet and a regular soda with the same market share, this implies that a consumer is equally likely to switch from product $k$ to products $j$ and $k$, even though we would expect a consumer of diet products to be more likely to switch to another diet product.
In contrast, under a nested logit specification, the cross-price elasticity with respect to product $k$ of all products in the same nest as product $k$ will be higher than the cross-price elasticity for products in other groups. Therefore, consumers who choose a diet soda are now more likely to substitute to another soda in the same nest (diet), compared to a regular soda. Note, however, that the elasticity is still constant within each group.
Problem 2(e)¶
Write a function that generates the Jacobian matrix of price derivatives $\Delta(p)$ (i.e., $\Delta_{ij} = \frac{\partial s_j}{p_i}$) for a given period, under the nested logit specification. Your function should take the time period, product variables, and model parameters as arguments, and return the Jacobian matrix. Print the Jacobian for the last time period in the sample (t = 100).
Solution¶
function build_jacobian_matrix_nested_logit(product_variables::DataFrame, alpha::Number, sigma::Number, time_period::Int)
dd = copy(product_variables)
dd = dd[dd.t .== time_period, :]
sort!(dd, [:nest, :product_ID])
grouped_data = groupby(dd, [:t, :nest])
nest_share_df = combine(grouped_data, :market_share => sum => :nest_share)
dd = leftjoin(dd, nest_share_df, on=[:t, :nest])
dd[!, :inside_share] = dd.market_share ./ dd.nest_share
dd[!, :same_nest] = dd.market_share + (sigma / (1 - sigma)) .* dd.inside_share
dd[!, :self_derivative] = alpha / (1 - sigma) .* dd.market_share .* (1 .- sigma .* dd.inside_share - (1 - sigma) .* dd.market_share)
Jacobian_part_same_nest = dd.same_nest * (-alpha .* dd.market_share)'
Jacobian_par_diff_nest = dd.market_share * (-alpha .* dd.market_share)'
Jacobian_matrix = Jacobian_part_same_nest
Jacobian_matrix[6:10,1:5] = Jacobian_par_diff_nest[6:10,1:5]
Jacobian_matrix[1:5,6:10] = Jacobian_par_diff_nest[1:5,6:10]
Jacobian_matrix[diagind(Jacobian_matrix)] = dd.self_derivative
return Jacobian_matrix
end
Jacobian_nested = build_jacobian_matrix_nested_logit(product_data, alpha_hat_nested, sigma_hat, 100)
latexify(round.(Jacobian_nested, digits=4))
Problem 3(a)¶
Assume that the supply side is a single-product Bertrand Nash Equilibrium (each firm owns only one product). That is, firm $f$ owning product $j$ solves the following problem in period $t$ to set prices:
$$ \max_{p_{jt}} \Pi_{ft} = p_{jt} q_{jt} - c_{jt} q_{jt} $$
Where $c_{jt}$ denotes product $j$'s marginal cost, and $q_{jt} = s_{jt} \bar{M}$. $\bar{M} = 10,000$ is the number of consumers in this market. Use the firms' FOCs to estimate the marginal cost for all $j$ and all $t$.
Solution¶
The FOC for firm $f$ owning product $j$ is given by:
$$ \frac{\partial \Pi_{ft}}{\partial p_{jt}} = s_{jt} + (p_{jt} - c_{jt})\times \frac{\partial s_{jt}}{\partial p_{jt}} = 0 $$
We can solve for $c_{jt}$ to get:
$$ c_{jt} = s_{jt} \times \Big (\frac{\partial s_{jt}}{\partial p_{jt}} \Big)^{-1} + p_{jt} $$
function get_marginal_cost(product_data::DataFrame, alpha, sigma)
# Compute price elasticities using the price_elasticity_nested_logit function
price_elasticities = price_elasticity_nested_logit(product_data, alpha, sigma)
# Merge the original product_data with the computed price elasticities
dd = innerjoin(product_data, price_elasticities, on=[:product_ID, :nest, :t])
# Calculate marginal_cost
dd[:, :marginal_cost] = (dd[:, :market_share] .+ dd[:, :price] .* dd[:, :price_derivative]) ./ dd[:, :price_derivative]
# Create result DataFrame
result = select(dd, :product_ID, :marginal_cost, :t)
return result
end
marginal_cost = get_marginal_cost(product_data, alpha_hat_nested, sigma_hat)
| Row | product_ID | marginal_cost | t |
|---|---|---|---|
| Int64 | Float64 | Int64 | |
| 1 | 1 | 0.876231 | 1 |
| 2 | 2 | 0.617729 | 1 |
| 3 | 3 | 1.82013 | 1 |
| 4 | 4 | 1.17669 | 1 |
| 5 | 5 | 1.07713 | 1 |
| 6 | 6 | 1.30661 | 1 |
| 7 | 7 | 2.8928 | 1 |
| 8 | 8 | 2.31099 | 1 |
| 9 | 9 | 1.95868 | 1 |
| 10 | 10 | 2.37663 | 1 |
| 11 | 1 | 1.37189 | 2 |
| 12 | 2 | 1.16414 | 2 |
| 13 | 3 | 1.62549 | 2 |
| ⋮ | ⋮ | ⋮ | ⋮ |
| 989 | 9 | 2.11077 | 99 |
| 990 | 10 | 1.74927 | 99 |
| 991 | 1 | 1.1073 | 100 |
| 992 | 2 | 1.63937 | 100 |
| 993 | 3 | 2.31675 | 100 |
| 994 | 4 | 1.57552 | 100 |
| 995 | 5 | 1.51497 | 100 |
| 996 | 6 | 2.1437 | 100 |
| 997 | 7 | 2.43904 | 100 |
| 998 | 8 | 2.17826 | 100 |
| 999 | 9 | 2.31966 | 100 |
| 1000 | 10 | 2.69466 | 100 |
Problem 3(b)¶
Now assume that the firms who own products 1 and 2 merge at $t = 100$, so that a single firm now owns both products. Simulate how the prices of all firms would change in this scenario.
Solution¶
The FOCs for firm $1$ are now given by:
$$ \frac{\partial \Pi_{1t}}{\partial p_{1t}} = s_{1t} + (p_{1t} - c_{1t})\times \frac{\partial s_{1t}}{\partial p_{1t}} + (p_{2t} - c_{2t}) \times \frac{\partial s_{2t}}{\partial p_{1t}} = 0 $$
$$ \frac{\partial \Pi_{1t}}{\partial p_{2t}} = s_{2t} + (p_{2t} - c_{2t})\times \frac{\partial s_{2t}}{\partial p_{2t}} + (p_{1t} - c_{1t}) \times \frac{\partial s_{1t}}{\partial p_{2t}} = 0 $$
The FOCs for firms $f = 3,...,10$ owning products $j = 3,,..,10$ are given by:
$$ \frac{\partial \Pi_{ft}}{\partial p_{jt}} = s_{jt} + (p_{jt} - c_{jt})\times \frac{\partial s_{jt}}{\partial p_{jt}} = 0 $$
This gives us a system with 10 equations (one for each product) and 10 unknowns ($p_{jt}$ for $j = 1,2,..,10$).
Note that each product's share $s_{jt}$ is a function of the vector of prices $\mathbf{p}_t$, the vectors of characteristics of all products ($s_{jt}$, $c_{jt}$, $Diet_j$, $Regular_j$), and of the unobserved product quality $\boldsymbol{\xi}_t$. We can estimate $\boldsymbol{\xi}_t$ as follows:
$$ \hat{\xi}_{jt} = \delta_{jt} - \hat{\alpha} p_{jt} - \hat{\beta}_1 s_{jt} - \hat{\beta}_2 c_{jt} - \hat{\gamma_d} Diet_j - \hat{\gamma_r} Regular_j $$
We then solve for the vector of prices that solves the system of equations to get the simulated vector of prices.
Alternatively, we may write the FOCs in matrix notation as:
$$ \mathbf{s}_t(\mathbf{p}_t) + (\Omega \circ \Delta(\mathbf{p}_t))(\mathbf{p}_t - \mathbf{c}_t) = 0 $$
Where $\mathbf{s}_t$ represents the vector of shares for all firms, $\mathbf{p}_t$ the vector of prices of all firms, $\Omega$ is an ownership matrix with element $ij$ equal to one if product $i$ and $j$ are owned by the same firm, $\Delta(p_t)$ is the Jacobian matrix derived in part 2(e), $\circ$ denotes the Hadamard product, and $\mathbf{c}_t$ is the vector of marginal costs of all firms.
xi_hat = nested_logit_result[:xi_hat]
# Construct firm_primitives DataFrame
firm_primitives = select(product_data, :product_ID, :nest, :sugar, :caffeine, :t)
# Merge with xi_hat DataFrame
firm_primitives = innerjoin(firm_primitives, xi_hat, on=[:product_ID, :t])
# merge with firm_primitives
firm_primitives = innerjoin(firm_primitives, marginal_cost, on=[:product_ID, :t])
initial_prices = product_data[product_data.t .== 100, :price]
model_parameters = Dict(
:alpha => coef(nested_logit_result[:coefficients])[6],
:beta_1 => coef(nested_logit_result[:coefficients])[1],
:beta_2 => coef(nested_logit_result[:coefficients])[2],
:gamma_d => coef(nested_logit_result[:coefficients])[3],
:gamma_r => coef(nested_logit_result[:coefficients])[4],
:sigma => coef(nested_logit_result[:coefficients])[5]
)
Dict{Symbol, Float64} with 6 entries:
:alpha => -0.466178
:beta_1 => 0.494104
:beta_2 => 0.490971
:gamma_r => -3.56714
:gamma_d => -1.53519
:sigma => 0.494244
function merger_sim_mat(model_parameters, firm_primitives, ownership_matrix, initial_guess)
alpha = model_parameters[:alpha]
beta_1 = model_parameters[:beta_1]
beta_2 = model_parameters[:beta_2]
gamma_d = model_parameters[:gamma_d]
gamma_r = model_parameters[:gamma_r]
sigma = model_parameters[:sigma]
dd = firm_primitives[firm_primitives.t .== 100, :]
sort!(dd, :product_ID)
function FOCs(par)
data = copy(dd)
data.diet = ifelse.(data.nest .== "Diet", 1, 0)
data.regular = ifelse.(data.nest .== "Regular", 1, 0)
data.price = par
data.diet = ifelse.(data.nest .== "Diet", 1, 0)
data.regular = ifelse.(data.nest .== "Regular", 1, 0)
data.delta = alpha .* data.price .+ beta_1 .* data.sugar .+ beta_2 .* data.caffeine .+ gamma_d .* data.diet .+ gamma_r .* data.regular .+ data.xi
data_grouped = combine(groupby(data, :nest),
:delta => (x -> sum(exp.(x ./ (1 - sigma)))) => :D_g)
data = leftjoin(data, data_grouped, on=:nest)
data.inside_share = exp.(data.delta ./ (1 - sigma)) ./ data.D_g
data.group_share = data.D_g.^(1 - sigma) ./ (data.D_g[1]^(1-sigma) + data.D_g[end]^(1-sigma) + 1)
data.market_share = data.inside_share .* data.group_share
Jacobian = build_jacobian_matrix_nested_logit(data, alpha, sigma, 100)
FOC_vector = data.market_share .+ (ownership_matrix .* Jacobian) * (data.price - data.marginal_cost)
return FOC_vector
end
solution = nlsolve(FOCs, initial_guess)
result = DataFrame(product_ID = dd.product_ID, nest = dd.nest, price = solution.zero)
return result
end
ownership_matrix = Matrix{Int}(I, 10, 10) # Create a mutable 10x10 identity matrix
ownership_matrix[1, 2] = 1
ownership_matrix[2, 1] = 1
merger_prices = merger_sim_mat(model_parameters, firm_primitives, ownership_matrix, initial_prices)
merger_prices
| Row | product_ID | nest | price |
|---|---|---|---|
| Int64 | String7 | Float64 | |
| 1 | 1 | Diet | 2.62634 |
| 2 | 2 | Diet | 3.15841 |
| 3 | 3 | Diet | 4.05187 |
| 4 | 4 | Diet | 2.73484 |
| 5 | 5 | Diet | 2.69931 |
| 6 | 6 | Regular | 3.25239 |
| 7 | 7 | Regular | 3.57224 |
| 8 | 8 | Regular | 3.54617 |
| 9 | 9 | Regular | 3.71483 |
| 10 | 10 | Regular | 3.91482 |
# Get original prices
original_prices_data = DataFrame(
product_ID = product_data[product_data.t .== 100, :product_ID],
original_price = product_data[product_data.t .== 100, :price]
)
# Merge merger_prices with original_prices
merger_prices = leftjoin(merger_prices, original_prices_data, on=:product_ID)
# Calculate the average price difference for different groups
merging_firms_diff = mean(merger_prices[merger_prices.product_ID .<= 2, :price] .- merger_prices[merger_prices.product_ID .<= 2, :original_price])
competitors_same_nest_diff = mean(merger_prices[(merger_prices.product_ID .> 2) .& (merger_prices.nest .== "Diet"), :price] .- merger_prices[(merger_prices.product_ID .> 2) .& (merger_prices.nest .== "Diet"), :original_price])
competitors_other_nest_diff = mean(merger_prices[(merger_prices.product_ID .> 2) .& (merger_prices.nest .== "Regular"), :price] .- merger_prices[(merger_prices.product_ID .> 2) .& (merger_prices.nest .== "Regular"), :original_price])
# Create result DataFrame
result = DataFrame(
Group = ["Merging products", "Competitors in same nest", "Competitors in a different nest"],
Avg_Change = [merging_firms_diff, competitors_same_nest_diff, competitors_other_nest_diff]
)
result
| Row | Group | Avg_Change |
|---|---|---|
| String | Float64 | |
| 1 | Merging products | 0.220639 |
| 2 | Competitors in same nest | 0.00777896 |
| 3 | Competitors in a different nest | 0.000393117 |
collusion_matrix = ones(10, 10)
collusion_prices = merger_sim_mat(model_parameters, firm_primitives, collusion_matrix, initial_prices)
collusion_prices
| Row | product_ID | nest | price |
|---|---|---|---|
| Int64 | String7 | Float64 | |
| 1 | 1 | Diet | 5.79755 |
| 2 | 2 | Diet | 6.32962 |
| 3 | 3 | Diet | 7.007 |
| 4 | 4 | Diet | 6.26577 |
| 5 | 5 | Diet | 6.20522 |
| 6 | 6 | Regular | 6.83395 |
| 7 | 7 | Regular | 7.12929 |
| 8 | 8 | Regular | 6.86851 |
| 9 | 9 | Regular | 7.00991 |
| 10 | 10 | Regular | 7.3849 |