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 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).
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.
multinomial_logit_coeff <- function(product_data, estimation = "IV"){
data <- copy(product_data)
data[, outside_share := 1 - sum(market_share), by = t]
data[, delta_jt := log(market_share) - log(outside_share)]
if (estimation == "IV"){
coefficients <- ivreg(delta_jt ~ 0 + price + sugar + caffeine + as.factor(nest) | 0 + sugar + caffeine + as.factor(nest) + caffeine_extract_price + corn_syrup_price, data = data)
} else if (estimation == "OLS"){
coefficients <- lm(delta_jt ~ 0 + price + sugar + caffeine + as.factor(nest), data = data)
} else {
stop("estimation must be either IV or OLS")
}
return(coefficients)
}
OLS_result <- multinomial_logit_coeff(product_data, estimation = "OLS")
summary(OLS_result)
##
## Call:
## lm(formula = delta_jt ~ 0 + price + sugar + caffeine + as.factor(nest),
## data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.87517 -0.61320 0.02783 0.63307 2.52055
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## price -1.62610 0.02102 -77.35 <2e-16 ***
## sugar 1.52806 0.04081 37.44 <2e-16 ***
## caffeine 1.44580 0.02862 50.52 <2e-16 ***
## as.factor(nest)Diet -2.44060 0.14861 -16.42 <2e-16 ***
## as.factor(nest)Regular -6.69212 0.24541 -27.27 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.8947 on 995 degrees of freedom
## Multiple R-squared: 0.9329, Adjusted R-squared: 0.9326
## F-statistic: 2769 on 5 and 995 DF, p-value: < 2.2e-16
Now assume that \(sug_{jt}\), $
caf_{jt}$, $ Diet_j$ and $ Regular_j$ are exogenous, but that \(p_{jt}\) is correlated with the unobserved
quality \(\xi_{jt}\). Use
caffeine_extract_price and corn_syrup_price as
instruments for prices, and estimate the parameters of the model. What
conditions must these two variables satisfy to be valid instruments?
Solution: If \(p_{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 caffeine_extract_price
and corn_syrup_price as instruments for prices. In order
for us to use these variables as instruments, they must satisfy the
usual IV assumptions:
Where \(z_{jt}\) is a vector with all the exogenous variables in our model and \(x_{jt}\) is a vector with \(sug_{jt}\), $ caf_{jt}$, $ Diet_j$, $ Regular_j$, and \(p_{jt}\). In our context, it implies that our instruments (input costs) must be correlated with prices, but uncorrelated with the unobserved product quality. Intuitively, input costs affect prices via higher marginal costs, but users only care about the caffeine and sugar content, not about how much the firm paid for these inputs.
#Let's simply use the function we created in part (a)
IV_result <- multinomial_logit_coeff(product_data, estimation = "IV")
summary(IV_result)
##
## Call:
## ivreg(formula = delta_jt ~ 0 + price + sugar + caffeine + as.factor(nest) |
## 0 + sugar + caffeine + as.factor(nest) + caffeine_extract_price +
## corn_syrup_price, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.96113 -0.57243 0.05239 0.61460 2.36528
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## price -1.41045 0.15057 -9.367 <2e-16 ***
## sugar 1.42768 0.08153 17.511 <2e-16 ***
## caffeine 1.38206 0.05332 25.918 <2e-16 ***
## as.factor(nest)Diet -2.55733 0.17585 -14.543 <2e-16 ***
## as.factor(nest)Regular -6.61816 0.26307 -25.158 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9409 on 995 degrees of freedom
## Multiple R-Squared: 0.9258, Adjusted R-squared: 0.9255
## Wald test: 1439 on 5 and 995 DF, p-value: < 2.2e-16
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}} \]
price_elasticity_mult_logit <- function(product_data, alpha){
data <- copy(product_data)
data[, price_derivative := alpha * market_share * (1 - market_share)]
data[, price_elasticities := price_derivative * (price / market_share)]
result <- data.table(product_ID = data$product_ID, nest = data$nest,
price_derivative = data$price_derivative, price_elasticities =
data$price_elasticities, t = data$t)
return(result)
}
price_elasticity_mult_logit_vec <- price_elasticity_mult_logit(product_data =
product_data, alpha = IV_result$coefficients[1])
price_elasticity_mult_logit_vec
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}} \]
cross_price_elasticity_mult_logit <- function(product_data, alpha, product){
data <- copy(product_data)
selected_product <- data[product_ID == product]
selected_product <- data.table(product_k_ID = selected_product$product_ID,
price_k = selected_product$price, share_k = selected_product$market_share,
t = selected_product$t)
data <- merge(data, selected_product, by = "t")
data[, cross_price_derivative := - alpha * market_share * share_k]
data[, cross_price_elasticities := cross_price_derivative * (price_k / market_share)]
data <- data[product_ID != product]
result <- data.table(product_ID = data$product_ID, nest = data$nest,
cross_price_derivative = data$cross_price_derivative,
cross_price_elasticities = data$cross_price_elasticities, t = data$t)
return(result)
}
cross_price_elasticity_mult_logit_vec <- cross_price_elasticity_mult_logit(product_data =
product_data, alpha = IV_result$coefficients[1], product = 1)
cross_price_elasticity_mult_logit_vec
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).
build_jacobian_matrix_mult_logit <- function(product_variables, alpha, time_period){
dd <- copy(product_variables)
dd <- dd[t == time_period]
dd <- dd[order(nest, product_ID)]
dd[, self_derivative := alpha * market_share * (1 - market_share)]
Jacobian_matrix <- dd[, outer(market_share, -alpha * market_share, "*")]
diag(Jacobian_matrix) <- dd[, self_derivative]
Jacobian_matrix <- as.data.table(Jacobian_matrix)
return(Jacobian_matrix)
}
Jacobian_mult <- build_jacobian_matrix_mult_logit(product_variables = product_data, alpha = IV_result$coefficients[1], time_period = 100)
Jacobian_mult
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 + \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.
nested_logit_coeff <- function(product_data){
data <- copy(product_data)
data[, outside_share := 1 - sum(market_share), by = t]
data[, delta_jt := log(market_share) - log(outside_share)]
data[, nest_share := sum(market_share), by = c("t", "nest") ]
data[, inside_share := market_share / nest_share]
data[, `:=`(sum_sugar = sum(sugar),
count = .N,
adj_sum = sum(sugar) - sugar,
adj_count = .N - 1),
by = .(t, nest)]
data[, `:=`(competitors_sugar = adj_sum / adj_count,
sum_sugar = NULL,
count = NULL,
adj_sum = NULL,
adj_count = NULL)]
data[, `:=`(sum_caffeine = sum(caffeine),
count = .N,
adj_sum = sum(caffeine) - caffeine,
adj_count = .N - 1),
by = .(t, nest)]
data[, `:=`(competitors_caffeine = adj_sum / adj_count,
sum_caffeine = NULL,
count = NULL,
adj_sum = NULL,
adj_count = NULL)]
setorder(data, t , product_ID)
coefficients <- ivreg(delta_jt ~ 0 + price + sugar + caffeine + log(inside_share) + as.factor(nest) |
0 + sugar + caffeine + as.factor(nest) + caffeine_extract_price + corn_syrup_price + competitors_sugar + competitors_caffeine, data = data)
xi <- coefficients$residuals
xi_hat <- data.table(product_ID = data$product_ID, xi = xi, t = data$t)
return(list(coefficients = coefficients, xi_hat = xi_hat))
}
Nested_logit_result <- nested_logit_coeff(product_data = product_data)
Nested_logit_coefficients <- Nested_logit_result$coefficients
summary(Nested_logit_coefficients)
##
## Call:
## ivreg(formula = delta_jt ~ 0 + price + sugar + caffeine + log(inside_share) +
## as.factor(nest) | 0 + sugar + caffeine + as.factor(nest) +
## caffeine_extract_price + corn_syrup_price + competitors_sugar +
## competitors_caffeine, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.76788 -0.17042 -0.01056 0.17251 0.78072
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## price -0.52250 0.03961 -13.191 < 2e-16 ***
## sugar 0.51846 0.02638 19.655 < 2e-16 ***
## caffeine 0.52071 0.02156 24.152 < 2e-16 ***
## log(inside_share) 0.68313 0.01578 43.299 < 2e-16 ***
## as.factor(nest)Diet 1.89879 0.11943 15.899 < 2e-16 ***
## as.factor(nest)Regular 0.85429 0.18350 4.656 3.67e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2606 on 994 degrees of freedom
## Multiple R-Squared: 0.9943, Adjusted R-squared: 0.9943
## Wald test: 1.584e+04 on 6 and 994 DF, p-value: < 2.2e-16
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}} \]
price_elasticity_nested_logit <- function(product_data, alpha , sigma){
data <- copy(product_data)
data[, nest_share := sum(market_share), by = c("t", "nest") ]
data[, inside_share := market_share / nest_share]
data[, price_derivative :=
( (alpha / (1 - sigma)) * market_share * (1 - sigma * inside_share -
(1 - sigma) * market_share) )]
data[, price_elasticities := price_derivative * (price / market_share)]
result <- data.table(product_ID = data$product_ID, nest = data$nest,
price_derivative = data$price_derivative, price_elasticities =
data$price_elasticities, t = data$t)
return(result)
}
own_price_elasticity_nested_logit <- price_elasticity_nested_logit(product_data =
product_data, alpha = Nested_logit_coefficients$coefficients[1], sigma =
Nested_logit_coefficients$coefficients[4])
own_price_elasticity_nested_logit
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 elasticity 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}} \]
cross_price_elasticity_nested_logit <- function(product_data, alpha, sigma, product){
data <- copy(product_data)
data[, nest_share := sum(market_share), by = c("t", "nest") ]
data[, inside_share := market_share / nest_share]
selected_product <- data[product_ID == product]
selected_product <- data.table(
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)
data <- merge(data, selected_product, by = "t")
data[, cross_price_derivative := ifelse(nest == nest_k,
- alpha * share_k * (market_share + (sigma/(1 - sigma))*inside_share),
-alpha * market_share * share_k )]
data[, cross_price_elasticities := cross_price_derivative * (price_k / market_share)]
data <- data[product_ID != product]
result <- data.table(
product_ID = data$product_ID, nest = data$nest,
cross_price_derivative = data$cross_price_derivative,
cross_price_elasticities = data$cross_price_elasticities,
t = data$t)
return(result)
}
cross_price_elasticity_nested_logit_vec <- cross_price_elasticity_nested_logit(
product_data = product_data, alpha = Nested_logit_coefficients$coefficients[1],
sigma = Nested_logit_coefficients$coefficients[4], product = 1)
cross_price_elasticity_nested_logit_vec
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 \(l\), 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.
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).
build_jacobian_matrix_nested_logit <- function(product_variables, alpha, sigma, time_period){
dd <- copy(product_variables)
dd <- dd[t == time_period]
dd <- dd[order(nest, product_ID)]
dd[, nest_share := sum(market_share), by = c("t", "nest") ]
dd[, inside_share := market_share / nest_share]
dd[, same_nest := (market_share + (sigma / (1 - sigma)) * inside_share )]
dd[, self_derivative := alpha / (1 - sigma) * market_share * (1 - sigma * inside_share - (1 - sigma) * market_share)]
Jacobian_part_same_nest <- dd[, outer(same_nest, -alpha * market_share, "*")]
Jacobian_par_diff_nest <- dd[, outer(market_share, -alpha * 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]
diag(Jacobian_matrix) <- dd[, self_derivative]
return(Jacobian_matrix)
}
Jacobian_nested <- build_jacobian_matrix_nested_logit(product_variables = product_data, time_period = 100, alpha = Nested_logit_coefficients$coefficients[1], sigma = Nested_logit_coefficients$coefficients[4])
Jacobian_nested
## [,1] [,2] [,3] [,4] [,5]
## [1,] -0.0885689105 3.356217e-04 2.466358e-03 0.0049083352 0.059147049
## [2,] 0.0003356217 -2.283637e-03 5.445228e-05 0.0001083663 0.001305849
## [3,] 0.0024663581 5.445228e-05 -1.643590e-02 0.0007963432 0.009596197
## [4,] 0.0049083352 1.083663e-04 7.963432e-04 -0.0319208495 0.019097532
## [5,] 0.0591470490 1.305849e-03 9.596197e-03 0.0190975319 -0.173622714
## [6,] 0.0012854646 2.838050e-05 2.085577e-04 0.0004150537 0.005001533
## [7,] 0.0146215539 3.228148e-04 2.372245e-03 0.0047210401 0.056890082
## [8,] 0.0031641791 6.985878e-05 5.133660e-04 0.0010216572 0.012311305
## [9,] 0.0013403545 2.959236e-05 2.174632e-04 0.0004327767 0.005215101
## [10,] 0.0011881255 2.623145e-05 1.927651e-04 0.0003836246 0.004622803
## [,6] [,7] [,8] [,9] [,10]
## [1,] 0.0012854646 0.0146215539 3.164179e-03 1.340354e-03 1.188126e-03
## [2,] 0.0000283805 0.0003228148 6.985878e-05 2.959236e-05 2.623145e-05
## [3,] 0.0002085577 0.0023722453 5.133660e-04 2.174632e-04 1.927651e-04
## [4,] 0.0004150537 0.0047210401 1.021657e-03 4.327767e-04 3.836246e-04
## [5,] 0.0050015331 0.0568900816 1.231130e-02 5.215101e-03 4.622803e-03
## [6,] -0.0610456608 0.0388943185 8.416930e-03 3.565433e-03 3.160494e-03
## [7,] 0.0388943185 -0.2908553981 9.573861e-02 4.055512e-02 3.594913e-02
## [8,] 0.0084169295 0.0957386071 -1.379629e-01 8.776336e-03 7.779575e-03
## [9,] 0.0035654332 0.0405551223 8.776336e-03 -6.350009e-02 3.295448e-03
## [10,] 0.0031604939 0.0359491289 7.779575e-03 3.295448e-03 -5.666243e-02
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} \]
get_marginal_cost <- function(product_data, alpha, sigma){
price_elasticities <- price_elasticity_nested_logit(product_data = product_data,
alpha = alpha, sigma = sigma)
dd <- merge(product_data, price_elasticities, by = c("product_ID", "nest", "t"))
dd[, marginal_cost := (market_share + price * price_derivative) * (1 / price_derivative) ]
result <- data.table(product_ID = dd$product_ID, marginal_cost = dd$marginal_cost, t = dd$t)
return(result)
}
marginal_cost <- get_marginal_cost(product_data = product_data,
alpha = Nested_logit_coefficients$coefficients[1],
sigma = Nested_logit_coefficients$coefficients[4])
marginal_cost
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 (\(sug_{jt}\), \(caf_{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 sug_{jt} - \hat{\beta}_2 caf_{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.
model_parameters <- list(alpha = Nested_logit_coefficients$coefficients[1],
beta_1 = Nested_logit_coefficients$coefficients[2], beta_2 =
Nested_logit_coefficients$coefficients[3], sigma = Nested_logit_coefficients$coefficients[4], gamma_d = Nested_logit_coefficients$coefficients[5], gamma_r = Nested_logit_coefficients$coefficients[6])
#We get the estimated xi_{jt} from the function we used for part 2(a)
xi_hat <- Nested_logit_result$xi_hat
firm_primitives <- data.table(product_ID = product_data$product_ID,
nest = product_data$nest, sugar = product_data$sugar, caffeine =
product_data$caffeine, t = product_data$t)
firm_primitives <- merge(firm_primitives, xi_hat, by = c("product_ID", "t"))
#firm_primitives contains the product_ID, nest, marginal cost, sweetness, healthiness,
# and unobserved product quality xi_{jt} of all products.
firm_primitives <- merge(firm_primitives, marginal_cost, by = c("product_ID", "t"))
#We will use the original prices as initial value for the solver
initial_prices <- product_data[t == 100][order(product_ID)]$price
ownership_matrix <- diag(10)
ownership_matrix[1,2] <- 1
ownership_matrix[2,1] <- 1
merger_sim_mat <- function(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[t == 100][order(product_ID)]
FOCs <- function(par){
dd[, diet := ifelse(nest == "Diet", 1, 0)]
dd[, regular := ifelse(nest == "Regular", 1, 0)]
dd[, price := par]
dd[, delta := alpha * price + beta_1 * sugar + beta_2 * caffeine + gamma_d * diet + gamma_r * regular + xi]
dd[, D_g := sum( exp(delta / (1 - sigma) ) ), by = nest]
dd[, inside_share := exp(delta / (1 - sigma)) / D_g]
dd[, group_share := D_g^(1 - sigma) / (D_g[1]^(1-sigma) + D_g[nrow(dd)]^(1-sigma) + 1)]
dd[, market_share := inside_share * group_share]
Jacobian <- build_jacobian_matrix_nested_logit(product_variables = dd, alpha = alpha, sigma = sigma, time_period = 100)
dd[, FOCs := market_share + (ownership_matrix * Jacobian) %*% (price - marginal_cost)]
return(dd[, FOCs ])
}
#Find W_ft that solve the problem
res = nleqslv(x = initial_guess, fn = FOCs,
method = c("Broyden"), global = c("cline"), control = list(allowSingular=TRUE, ftol = 1e-10))
result <- data.table(product_ID = dd$product_ID, nest = dd$nest, price = res$x)
return(result)
}
merger_prices <- merger_sim_mat(model_parameters = model_parameters,
firm_primitives = firm_primitives, initial_guess = initial_prices, ownership_matrix = ownership_matrix)
merger_prices
How does the average price of the products from the merging firms change after the merger? How does the average price of competing products in the same nest change after the merger? How does the average price for competing products in a different nest change after the merger?
Solution:
original_prices <- data.table(product_ID =
product_data[t == 100]$product_ID, original_price =
product_data[t == 100]$price)
merger_prices <- merge(merger_prices, original_prices, by = "product_ID")
merging_firms_diff <- merger_prices[product_ID <=2][,
mean(price - original_price)]
competitors_same_nest_diff <- merger_prices[product_ID > 2 & nest == "Diet"][,
mean(price - original_price)]
competitors_other_nest_diff <- merger_prices[product_ID > 2 & nest == "Regular"][,
mean(price - original_price)]
result <- data.table(Group = c("Merging products", "Competitors in same nest",
"Competitors in a different nest"), Avg_Change = c(merging_firms_diff,
competitors_same_nest_diff, competitors_other_nest_diff))
result
Now instead assume that all firms collude during the last period (t = 100) and choose the prices that maximize the sum of all profits. Simulate how the prices of all firms would change in this scenario.
Solution:
Note that since firms maximize the sum of all firms’ profits, the equilibrium solution will be equivalent to the one we would have under a hypothetical merger between all firms. Therefore, all we have to do is replace the ownership matrix with a \(10 \times 10\) matrix with all elements equal to \(1\), and we can use the same function we used in part 3(b).
collusion_matrix <- matrix(1, nrow = 10, ncol = 10)
collusion_prices <- merger_sim_mat(model_parameters = model_parameters,
firm_primitives = firm_primitives, initial_guess = initial_prices, ownership_matrix = collusion_matrix)
collusion_prices