using CSV
using DataFrames
using StringEncodings
using FixedEffectModels
using RegressionTables
using Plots
using LinearAlgebra
using Statistics
using Optim
using Printf
using ForwardDiff
using Random
using GLM
using Serialization
3 基礎編 2
mutable struct datalist_struct
::Array{Float64,2};
X1::Array{Float64,2};
X2::Array{Float64,2};
Z::Vector{Float64};
ShareVec::Vector{Int64};
marketIndex::Vector{Float64};
logitshare::Array{Float64,2};
randomDrawMat::BitMatrix
marketIndexMatend
3.1 関数の読み込み
for file in readdir("functions/demand_estimation")
include("functions/demand_estimation/" * file)
end
3.2 前回からの加工済みデータの読み込み
= CSV.read("tmp/demand_estimation_1/data.csv", DataFrame); data
3.3 データの準備
= [
NIPPYOautoIDvec 260, 4, 76, 104, 64, 54, 152, 153, 71, 197,
42, 45, 114, 208, 209, 77, 236, 58, 127, 187,
79, 175, 19, 117, 216, 112, 256, 119, 37, 158
];
= data[
dataNIPPYO in(NIPPYOautoIDvec).(data[:, :NameID]),
:year, :share, :NameID, :Sales, :price, :hppw, :FuelEfficiency, :size, :Name]
[
];:log_sales] = log.(dataNIPPYO[:, :Sales]);
dataNIPPYO[!, :log_price] = log.(dataNIPPYO[:, :price]);
dataNIPPYO[!, :log10_sales] = log10.(dataNIPPYO[:, :Sales]);
dataNIPPYO[!, :log10_price] = log10.(dataNIPPYO[:, :price]); dataNIPPYO[!,
3.4 Logitモデルにおける価格弾力性行列の作成
:logit_share] = log.(data[:, :share]) .- log.(data[:, :share0]);
data[!,
= reg(
resultGH
data, @formula(logit_share ~ (
~ iv_GH_own_hppw + iv_GH_own_FuelEfficiency + iv_GH_own_size +
price + iv_GH_other_FuelEfficiency + iv_GH_other_size
iv_GH_other_hppw + hppw + FuelEfficiency + size),
) robust(),
Vcov.= true
save
);
= dataNIPPYO[
data2016 .== 2016,
dataNIPPYO.year :price, :share, :NameID, :Name]
[
];= nrow(data2016);
numCars2016
= (
ownElasticity .== "price"][1] .*
resultGH.coef[resultGH.coefnames .*
data2016.price 1.0 .- data2016.share)
(
);= (
crossElasticity - resultGH.coef[resultGH.coefnames .== "price"][1] .*
.*
data2016.price
data2016.share
);
= reduce(
elasticityMat
hcat, = 1:numCars2016]
[crossElasticity for car
);diagind(elasticityMat)] = ownElasticity;
elasticityMat[
12, 13, 10, 1], [12, 13, 10, 1]] elasticityMat[[
4×4 Matrix{Float64}:
-1.76455 0.00114929 0.00114929 0.00114929
0.00122042 -0.818689 0.00122042 0.00122042
0.000148175 0.000148175 -0.959449 0.000148175
0.00184509 0.00184509 0.00184509 -0.67175
3.5 入れ子型ロジット推定のためのBLP操作変数の作成
transform!(
groupby(data, [:year, :Maker, :Type]),
(:hppw, :FuelEfficiency, :size] .=>
[.=>
sum :hppw_sum_own, :FuelEfficiency_sum_own, :size_sum_own]
[
),
(:hppw, :FuelEfficiency, :size] .=>
[-> sum(x.^2)) .=>
(x :hppw_sqr_sum_own, :FuelEfficiency_sqr_sum_own, :size_sqr_sum_own]
[
),=> :group_n
nrow
);transform!(
groupby(data, [:year, :Type]),
(:hppw, :FuelEfficiency, :size] .=>
[.=>
sum :hppw_sum_mkt, :FuelEfficiency_sum_mkt, :size_sum_mkt]
[
),
(:hppw, :FuelEfficiency, :size] .=>
[-> sum(x.^2)) .=>
(x :hppw_sqr_sum_mkt, :FuelEfficiency_sqr_sum_mkt, :size_sqr_sum_mkt]
[
),=> :mkt_n
nrow
);
for variable in ["hppw", "FuelEfficiency", "size"]
addIVColumnsForNestedLogit!(data, variable)
end
:iv_BLP_own_num_nest] = data[:, :group_n] .- 1;
data[!, :iv_BLP_other_num_nest] = data[:, :mkt_n] .- data[:, :group_n]; data[!,
3.6 入れ子型ロジットモデルの推定
= transform(
data groupby(data, [:year, :Type]),
:Sales => sum => :sum_year_body
);:inside_share] = data.Sales ./ data.sum_year_body;
data[!, :log_inside_share] = log.(data.Sales ./ data.sum_year_body);
data[!, = reg(
resultOLS
data, @formula(
~
logit_share + log_inside_share + hppw + FuelEfficiency + size
price
)
);
= reg(
resultBLPNested
data, @formula(logit_share ~ (
+ log_inside_share ~ iv_BLP_own_hppw_nest + iv_BLP_own_FuelEfficiency_nest + iv_BLP_own_size_nest +
price + iv_BLP_other_FuelEfficiency_nest + iv_BLP_other_size_nest +
iv_BLP_other_hppw_nest + iv_BLP_other_num_nest
iv_BLP_own_num_nest + hppw + FuelEfficiency + size),
) robust()
Vcov.
);
regtable(resultOLS, resultBLPNested)
-----------------------------------------------
logit_share
---------------------
(1) (2)
-----------------------------------------------
(Intercept) -7.557*** -9.548***
(0.166) (0.239)
price -0.307*** -0.654***
(0.013) (0.053)
log_inside_share 0.782*** 0.595***
(0.009) (0.035)
hppw 10.636*** 18.925***
(0.657) (1.965)
FuelEfficiency 0.055*** 0.069***
(0.004) (0.006)
size 0.156*** 0.227***
(0.008) (0.012)
-----------------------------------------------
Estimator OLS IV
-----------------------------------------------
N 1,823 1,823
R2 0.862 0.765
Within-R2
First-stage F statistic 1.667
-----------------------------------------------
3.6.1 入れ子型ロジットモデルにおける自己価格弾力性の計算
= resultOLS.coef[resultOLS.coefnames .== "price"][1]
alpha1 = resultOLS.coef[resultOLS.coefnames .== "log_inside_share"][1]
sigma1
= resultBLPNested.coef[resultBLPNested.coefnames .== "price"][1]
alpha2 = resultBLPNested.coef[resultBLPNested.coefnames .== "log_inside_share"][1]
sigma2
:own_elas_ols] = alpha1 .* data[:, :price] .* (
data[!, 1.0 .- sigma1 .* data[:, :inside_share] .-
1.0 .- sigma1) .* data[:, :share]
(./ (1.0 .- sigma1);
) :own_elas_ivblp_nested] = alpha2 .* data[:, :price] .* (
data[!, 1.0 .- sigma2 .* data[:, :inside_share] .-
1.0 .- sigma2) .* data[:, :share]
(./ (1.0 .- sigma2);
)
describe(data[:, r"^own_elas"], :mean, :std, :median, :min, :max)
2×6 DataFrame
Row | variable | mean | std | median | min | max |
---|---|---|---|---|---|---|
Symbol | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | own_elas_ols | -3.52131 | 2.55849 | -2.84383 | -17.7936 | -0.96257 |
2 | own_elas_ivblp_nested | -4.04935 | 2.93775 | -3.2664 | -20.4015 | -1.11262 |
= data[
dataNIPPYO in(NIPPYOautoIDvec).(data[:, :NameID]),
[:year, :share, :Type, :inside_share,
:NameID, :Sales, :price, :hppw, :FuelEfficiency, :size, :Name
]
];:log_sales] = log.(dataNIPPYO[:, :Sales]);
dataNIPPYO[!, :log_price] = log.(dataNIPPYO[:, :price]);
dataNIPPYO[!,
= dataNIPPYO[
data2016 .== 2016,
dataNIPPYO.year :price, :Type, :share, :inside_share, :NameID, :Name]
[
];= nrow(data2016);
numCars2016
= (
ownElasticityNestedLogit .*
alpha2 .*
data2016.price
(1.0 .- sigma2 .*
.-
data2016.inside_share 1.0 .- sigma2) .*
(
data2016.share./
) 1.0 .- sigma2)
(
);= (
crossElasticityOtherGroup - alpha2 .*
.*
data2016.price
data2016.share
);
= reduce(
crossElasticityOtherGroup
hcat, = 1:numCars2016]
[crossElasticityOtherGroup for car
);
crossElasticityOtherGroup[diagind(crossElasticityOtherGroup)
= ownElasticityNestedLogit; ]
= reduce(
crossElasticitySameGroup
hcat,
[- alpha2 .* data2016.price .* (
.* data2016.inside_share .+ (1.0 .- sigma2) .* data2016.share
sigma2 ./ (1.0 .- sigma2)
) in 1:numCars2016]
for car
);
= (
sameGroupIndicatorMat reduce(hcat, [data2016.Type for car = 1:numCars2016]) .==
permutedims(reduce(hcat, [data2016.Type for car = 1:numCars2016]))
);= (sameGroupIndicatorMat .== 0);
otherGroupIndicatorMat
= (
elasticityNestedLogitMat .*
crossElasticitySameGroup .+
sameGroupIndicatorMat .*
crossElasticityOtherGroup
otherGroupIndicatorMat
);
elasticityNestedLogitMat[diagind(elasticityNestedLogitMat)
= ownElasticityNestedLogit;
]
12, 13, 10, 1], [12, 13, 10, 1]] elasticityNestedLogitMat[[
4×4 Matrix{Float64}:
-5.1197 0.0477573 0.0477573 0.00136172
0.050713 -2.34881 0.050713 0.001446
0.00615725 0.00615725 -2.80217 0.000175564
0.00218614 0.00218614 0.00218614 -1.83296
3.7 ランダム係数ロジットモデルの推定
3.7.1 下準備
sort!(data, [:year, :NameID]);
= nrow(data);
N = length(unique(data.year));
T
= hcat(ones(N), Matrix(data[:, [:price, :FuelEfficiency, :hppw, :size]]));
X1 = hcat(data.price, ones(N), data.size);
X2 = hcat(
Z ones(N),
Matrix(data[:, [:FuelEfficiency, :hppw, :size]]),
Matrix(data[:, r"^iv_GH.*(?<!nest)$"])
);
.seed!(123);
Random= 500; Nsim
= randn(size(X2)[2], Nsim);
randomDrawMat
= data.year;
marketIndex
= sort(unique(data.year));
uniqueMarketIndex = reduce(
marketIndexMat
hcat, .== market for market in uniqueMarketIndex]
[marketIndex
);
= datalist_struct(
datalist
X1,
X2,
Z,
data.share,
marketIndex,
data.logit_share,
randomDrawMat,
marketIndexMat );
3.7.2 推定
ここでの推定結果が元のサポートサイトのものとかなり異なることに注意されたい。 元の推定では、ランダム係数の推計値が初期値からほとんど変わっていないように見える。 もしかすると、本のコードでは市場シェアの更新(永続代入演算子が用いられている)が正しくされていないのかもしれない。
= [0.3, 18, 0.01];
initial_x = calculateMeanUtil(initial_x, datalist, datalist.logitshare); delta_ini
= OnceDifferentiable(
objFunc_for_Optim -> calculateGMMObjective(x, datalist, delta_ini),
x
initial_x;= :forward
autodiff
);@time resultGMM = optimize(
objFunc_for_Optim,0.0, 0.0, 0.0],
[Inf, Inf, Inf],
[
initial_x,Fminbox(LBFGS()),
Options(show_trace = false)
Optim. );
resultGMM.minimizer
3-element Vector{Float64}:
0.6407730207977352
14.72488892325358
1.689052459807443e-11
= inv(datalist.Z' * datalist.Z);
W = calculateMeanUtil(resultGMM.minimizer, datalist, delta_ini);
delta = (
beta_hat ' * datalist.Z * W * datalist.Z' * datalist.X1) \
(datalist.X1' * datalist.Z * W * datalist.Z' * delta)
(datalist.X1
);
beta_hat
5-element Vector{Float64}:
-31.984921285756943
-1.0841982623293536
0.1076677873797389
8.581036411670775
0.28298440012892323
3.7.3 標準誤差の計算
= delta - X1 * beta_hat;
Xi = reduce(+, Z[i,:] * Z[i,:]' .* Xi[i]^2 ./ N for i = 1:N);
Omega_hat = ForwardDiff.jacobian(
Ddelta -> calculateMeanUtil(x, datalist, delta),
x
resultGMM.minimizer
);= Z' * hcat(- X1, Ddelta) ./ N;
G = (G' * W * G) \ G' * W * Omega_hat * W * G * inv(G' * W * G);
AsyVarMat = sqrt.(diag(AsyVarMat) ./ N);
Ase
DataFrame(
= [
Var "Const", "Price", "Fuel Efficiency", "hppw",
"size", "random_price", "random_constant", "random_size"
],= vcat(beta_hat, resultGMM.minimizer),
Est = Ase
se )
8×3 DataFrame
Row | Var | Est | se |
---|---|---|---|
String | Float64 | Float64 | |
1 | Const | -31.9849 | 6.91814 |
2 | Price | -1.0842 | 1.34743 |
3 | Fuel Efficiency | 0.107668 | 0.0115801 |
4 | hppw | 8.58104 | 3.13587 |
5 | size | 0.282984 | 0.127824 |
6 | random_price | 0.640773 | 0.845573 |
7 | random_constant | 14.7249 | 21.1974 |
8 | random_size | 1.68905e-11 | 1.79294 |
3.8 価格弾力性行列の計算
= data.year .== 2016;
market2016Index
= calculateElasticity(
elasmat :].price,
data[market2016Index, :],
X2[market2016Index,
beta_hat,
resultGMM.minimizer,
randomDrawMat,:]
delta[market2016Index,
)
59, 80, 102, 113], [59, 80, 102, 113]] elasmat[[
4×4 Matrix{Float64}:
-2.07694 0.0043804 0.00405532 0.00447937
0.0204111 -1.92602 0.0185025 0.0248292
0.00413169 0.00404559 -2.14446 0.00376725
0.00295524 0.0035155 0.00243949 -1.51216
3.9 応用: プライシング
以下で求められる値はサポートサイトのものとかなり異なるが、これは上で述べた推定結果が原因だと思われる。
= range(1.8, 4.0, step = 0.05);
price_range = calculateRevenue.(
ownpi_res
price_range, Ref(data), Ref(datalist), Ref(delta), Ref(beta_hat), Ref(resultGMM.minimizer),
"ownpi"
);
plot(price_range, ownpi_res, legend = false)
xlabel!("Price")
ylabel!("Revenue")
= range(1.8, 4.0, step = 0.05);
price_range = calculateRevenue.(
totalpi_res
price_range, Ref(data), Ref(datalist), Ref(delta), Ref(beta_hat), Ref(resultGMM.minimizer),
"totalpi"
);
plot(price_range, totalpi_res, legend = false)
xlabel!("Price")
ylabel!("Revenue")
= optimize(
ownpi_optim_res -> - calculateRevenue(
x 1], data, datalist, delta,
x["ownpi"
beta_hat, resultGMM.minimizer,
),3.0]
[
);
@printf("Revenue-maximizing price: %.3f \n", ownpi_optim_res.minimizer[1])
@printf("Maximized revenue : %.3f", -ownpi_optim_res.minimum)
Revenue-maximizing price: 3.256
Maximized revenue : 44811.735
= optimize(
totalpi_optim_res -> - calculateRevenue(
x 1], data, datalist, delta,
x["totalpi"
beta_hat, resultGMM.minimizer,
),3.0]
[
);
@printf("Revenue-maximizing price: %.3f \n", totalpi_optim_res.minimizer[1])
@printf("Maximized revenue : %.3f", -totalpi_optim_res.minimum)
Revenue-maximizing price: 3.508
Maximized revenue : 570181.486
calculateRevenue(
1],
ownpi_optim_res.minimizer[
data,
datalist,
delta,
beta_hat,
resultGMM.minimizer,"totalpi"
)
569858.7798782628