using CSV
using LinearAlgebra
using Random
using StatsBase
using Distributions
using DataFrames
using Plots
using ShiftedArrays
using ForwardDiff
using Optim
using Serialization
7 基礎編
for file in readdir("functions/single_agent_dynamic/")
include("functions/single_agent_dynamic/" * file)
end
7.1 パラメタの設定
= [0.004, 0.003];
theta_true
= 0.99;
beta = Float64(MathConstants.eulergamma);
Euler_const
= 2; num_choice
= 2000:100:2500;
price_states = 0:5:100;
mileage_states = length(price_states);
num_price_states = length(mileage_states);
num_mileage_states = num_price_states * num_mileage_states;
num_states
= Matrix(
states_matrix reshape(
reinterpret(
Int,
collect(Iterators.product(price_states, mileage_states))
),2, :)
('
) );
= [0.25, 0.05];
kappa_true
= generateMileageTransition(
mileage_trans_mat_true
kappa_true,
num_mileage_states
);
1:4, 1:4, 1] mileage_trans_mat_true[
4×4 Matrix{Float64}:
0.7 0.25 0.05 0.0
0.0 0.7 0.25 0.05
0.0 0.0 0.7 0.25
0.0 0.0 0.0 0.7
= [
lambda_true 0.1, 0.2, 0.2, 0.2, 0.2,
0.1, 0.2, 0.2, 0.2, 0.2,
0.1, 0.1, 0.2, 0.2, 0.1,
0.1, 0.1, 0.2, 0.2, 0.1,
0.05, 0.05, 0.1, 0.1, 0.2,
0.05, 0.05, 0.1, 0.1, 0.2
];
= generatePriceTransition(
price_trans_mat_true
lambda_true,
num_price_states );
= Array{Float64}(
trans_mat_true undef, num_states, num_states, num_choice
);for i in 1:num_choice
:, :, i] = kron(
trans_mat_true[:, :, i],
mileage_trans_mat_true[
price_trans_mat_true
);end
= eigvecs(price_trans_mat_true');
price_trans_eigen = (
price_dist_steady :, end] / sum(price_trans_eigen[:, end])
price_trans_eigen[ )
6-element Vector{Float64}:
0.07377049180327863
0.07377049180327883
0.1639344262295082
0.16393442622950818
0.28571428571428564
0.23887587822014061
@time EV_true = calculateEV(
theta_true, beta, trans_mat_true, states_matrix );
0.727990 seconds (2.26 M allocations: 202.622 MiB, 9.86% gc time, 90.73% compilation time)
= calculateFlowUtil(theta_true, states_matrix);
U_true = U_true + beta .* EV_true; V_CS_true
= reshape(
prob_buy_true_mat exp.(V_CS_true[:, 2]) ./ sum(exp.(V_CS_true), dims = 2),
(num_price_states, num_mileage_states) );
7.2 データの生成
= 1000;
num_consumer
= 12 * 50;
num_period = 12 * 10;
num_period_obs
= num_consumer * num_period;
num_obs
Random.seed!(42);
= reduce(vcat, [generateData(
data_gen
consumer_id,
V_CS_true,
trans_mat_true,
price_dist_steadyin 1:num_consumer]) |>
) for consumer_id filter(:period => (x -> x > num_period - num_period_obs))
:price] = states_matrix[data_gen.state, 1];
data_gen[!, :mileage] = states_matrix[data_gen.state, 2]; data_gen[!,
describe(
:, [:price, :mileage, :action]], :all
data_gen[:, [:variable, :mean, :std, :min, :max]] )[
3×5 DataFrame
Row | variable | mean | std | min | max |
---|---|---|---|---|---|
Symbol | Float64 | Float64 | Int64 | Int64 | |
1 | price | 2322.63 | 151.826 | 2000 | 2500 |
2 | mileage | 33.0198 | 23.6924 | 0 | 100 |
3 | action | 0.02915 | 0.168228 | 0 | 1 |
histogram(data_gen.price, bar_width = 50, legend=false)
xlabel!("Price (thousand JPY)")
ylabel!("Frequency")
histogram(
data_gen.mileage, = 3,
bar_width =false,
legend= [collect(mileage_states); [105]] .- 2.5
bins
)xlabel!("Mileage (thousand km)")
ylabel!("Frequency")
bar(
price_states,combine(groupby(data_gen, :price), :action => mean).action_mean,
= 50,
bar_width =false
legend
)xlabel!("Price (thousand JPY)")
ylabel!("Purchase probability")
bar(
mileage_states,combine(groupby(data_gen, :mileage), :action => mean).action_mean,
= 3,
bar_width =false
legend
)xlabel!("Mileage (thousand km)")
ylabel!("Purchase probability")
次の図は「観測された条件付き購入確率」を示す。 元のサポートサイトでは3Dプロットを使って示されていたが、Juliaではいい感じの3Dプロットを書くのが難しかったため、代わりにヒートマップで示している。
heatmap(
mileage_states,
price_states,reshape(
combine(
groupby(data_gen, [:mileage, :price]),
:action => mean
).action_mean,
(num_price_states, num_mileage_states)
),= "Mileage",
xlabel = "Price",
ylabel = "Conditional probability of buying"
title )
7.3 遷移行列の推定
transform!(
groupby(data_gen, :consumer_id),
:price, :mileage, :action] .=> ShiftedArrays.lag
[ );
= combine(
num_cond_obs_mileage groupby(
transform(
|>
data_gen filter(
:period =>
-> x != (num_period - num_period_obs + 1))
(x
),:mileage_lag, :mileage, :action_lag] =>
[ByRow(
->
(mileage_lag, mileage, action_lag)
(
(== 0) &
(action_lag 5 <= mileage_lag <= 95) &
(== mileage)
(mileage_lag |
) == 1) & (mileage == 0))
((action_lag "cond_obs_mileage1" :
) ?
(
(== 0) &
(action_lag 5 <= mileage_lag <= 90) &
(== mileage - 5)
(mileage_lag |
) == 1) & (mileage == 5))
((action_lag "cond_obs_mileage2" :
) ?
(
(== 0) &
(action_lag 5 <= mileage_lag <= 90) &
(== mileage - 10)
(mileage_lag |
) == 1) & (mileage == 10))
((action_lag "cond_obs_mileage3" :
) ?
(
(== 0) &
(action_lag == 95) &
(mileage_lag == 100)
(mileage
)"cond_obs_mileage4" :
) ? "other"
=>
) :cond_obs_mileage
),:cond_obs_mileage]
[
),=> :num_cond_obs
nrow |> filter(:cond_obs_mileage => (x -> (x != "other")));
)
= Dict(
num_cond_obs_mileage => v[1, "num_cond_obs"]
k for ((k, ), v) in pairs(groupby(num_cond_obs_mileage, :cond_obs_mileage))
);
= zeros(2);
kappa_est
1] = num_cond_obs_mileage["cond_obs_mileage2"] * (
kappa_est["cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"] +
num_cond_obs_mileage["cond_obs_mileage4"]
num_cond_obs_mileage[/ (
)
("cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"]
num_cond_obs_mileage[* (
) "cond_obs_mileage1"] +
num_cond_obs_mileage["cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"] +
num_cond_obs_mileage["cond_obs_mileage4"]
num_cond_obs_mileage[
)
);
2] = num_cond_obs_mileage["cond_obs_mileage3"] * (
kappa_est["cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"] +
num_cond_obs_mileage["cond_obs_mileage4"]
num_cond_obs_mileage[/ (
)
("cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"]
num_cond_obs_mileage[* (
) "cond_obs_mileage1"] +
num_cond_obs_mileage["cond_obs_mileage2"] +
num_cond_obs_mileage["cond_obs_mileage3"] +
num_cond_obs_mileage["cond_obs_mileage4"]
num_cond_obs_mileage[
)
);
kappa_est
2-element Vector{Float64}:
0.2507060588583926
0.05038686722769128
= zeros((2, 2));
Infomat_mileage_est
1, 1] = (
Infomat_mileage_est[
("cond_obs_mileage1"] /
num_cond_obs_mileage[1 - kappa_est[1] - kappa_est[2])^2
(+
) "cond_obs_mileage2"] / kappa_est[1]^2) +
(num_cond_obs_mileage[
("cond_obs_mileage4"] /
num_cond_obs_mileage[1] + kappa_est[2])^2
(kappa_est[
)
);
1, 2] = (
Infomat_mileage_est[
("cond_obs_mileage1"] /
num_cond_obs_mileage[1 - kappa_est[1] - kappa_est[2])^2
(+
)
("cond_obs_mileage4"] /
num_cond_obs_mileage[1] + kappa_est[2])^2
(kappa_est[
)
);
2, 1] = Infomat_mileage_est[1, 2];
Infomat_mileage_est[
2, 2] = (
Infomat_mileage_est[
("cond_obs_mileage1"] /
num_cond_obs_mileage[1 - kappa_est[1] - kappa_est[2])^2
(+
) "cond_obs_mileage3"] / kappa_est[2]^2) +
(num_cond_obs_mileage[
("cond_obs_mileage4"] /
num_cond_obs_mileage[1] + kappa_est[2])^2
(kappa_est[
)
);
= sqrt.(diag(inv(Infomat_mileage_est))); kappa_se
DataFrame(kappa_est = kappa_est, kappa_se = kappa_se)
2×2 DataFrame
Row | kappa_est | kappa_se |
---|---|---|
Float64 | Float64 | |
1 | 0.250706 | 0.0013098 |
2 | 0.0503869 | 0.000662066 |
= combine(
num_cond_obs_price groupby(
|>
data_gen filter(
:period =>
-> x != (num_period - num_period_obs + 1))
(x
),:price_lag, :price]
[
),=> :num_cond_obs
nrow
)
= [
num_cond_obs_price
(|>
num_cond_obs_price filter(
:price_lag, :price] =>
[
(->
(price_lag, price) == p_lag) & (price == p))
((price_lag
)
)1, :num_cond_obs]
)[in price_states, p in price_states
for p_lag
]
= num_cond_obs_price ./ sum(num_cond_obs_price, dims = 2);
lambda_est_mat = lambda_est_mat'[lambda_est_mat' .!= diag(lambda_est_mat)]; lambda_est
= vcat([
lambda_se sqrt.(diag(inv(
(diagm(num_cond_obs_price[i, :])[Not(i), Not(i)] ./
Not(i), Not(i)].^2
lambda_est_mat[.+ (
) /
num_cond_obs_price[i, i] ^2
lambda_est_mat[i, i]
)in 1:num_price_states
))) for i ...) ]
30-element Vector{Float64}:
0.003331377707369882
0.00671087202774432
0.006726990888800794
0.009561549640256135
0.009611051522479718
0.003237157226120388
0.006683210833495453
0.006696372126260586
0.009621285444288651
0.009781740363963828
0.002223144478519486
0.0022780260513659977
0.004516183865925486
⋮
0.006813746538738526
0.007677243174949639
0.002356517399713087
0.0023843478474719036
0.0045057964689017155
0.0045121986971131695
0.005002569765090251
0.0026110065055348717
0.0025918295553672807
0.004885082439414729
0.004962738202957978
0.005515586477784162
DataFrame(
= lambda_est,
lambda_est = lambda_se
lambda_se )
30×2 DataFrame
5 rows omitted
Row | lambda_est | lambda_se |
---|---|---|
Float64 | Float64 | |
1 | 0.101205 | 0.00333138 |
2 | 0.196952 | 0.00671087 |
3 | 0.198431 | 0.00672699 |
4 | 0.205254 | 0.00956155 |
5 | 0.198886 | 0.00961105 |
6 | 0.104459 | 0.00323716 |
7 | 0.200023 | 0.00668321 |
8 | 0.201733 | 0.00669637 |
9 | 0.20447 | 0.00962129 |
10 | 0.188505 | 0.00978174 |
11 | 0.0998312 | 0.00222314 |
12 | 0.0979389 | 0.00227803 |
13 | 0.205646 | 0.00451618 |
⋮ | ⋮ | ⋮ |
19 | 0.200477 | 0.00681375 |
20 | 0.101128 | 0.00767724 |
21 | 0.0495297 | 0.00235652 |
22 | 0.0498236 | 0.00238435 |
23 | 0.1 | 0.0045058 |
24 | 0.100911 | 0.0045122 |
25 | 0.199265 | 0.00500257 |
26 | 0.0486584 | 0.00261101 |
27 | 0.0509654 | 0.00259183 |
28 | 0.103705 | 0.00488508 |
29 | 0.100511 | 0.00496274 |
30 | 0.198112 | 0.00551559 |
7.4 パラメタの推定
7.4.1 静学的なロジットによる推定
@time logit_stat_opt = optimize(
-> - calculateLogLikelihoodStatic(x, states_matrix, data_gen),
x
theta_true,Options(show_trace = false)
Optim. )
2.115396 seconds (71.91 M allocations: 1.319 GiB, 6.80% gc time, 21.10% compilation time)
* Status: success
* Candidate solution
Final objective value: 1.350416e+04
* Found with
Algorithm: Nelder-Mead
* Convergence measures
√(Σ(yᵢ-ȳ)²)/n ≤ 1.0e-08
* Work counters
Seconds run: 2 (vs limit Inf)
Iterations: 48
f(x) calls: 98
= logit_stat_opt.minimizer;
theta_est_stat
= ForwardDiff.hessian(
hessian_stat -> - calculateLogLikelihoodStatic(x, states_matrix, data_gen),
x
logit_stat_opt.minimizer
);
= sqrt.(diag(inv(hessian_stat))); theta_se_stat
DataFrame(theta_est_stat = theta_est_stat, theta_se_stat = theta_se_stat)
2×2 DataFrame
Row | theta_est_stat | theta_se_stat |
---|---|---|
Float64 | Float64 | |
1 | 0.0428172 | 0.000692099 |
2 | 0.00237647 | 1.9178e-5 |
7.4.2 不動点アルゴリズムを用いた、動学的なロジットによる推定
= generateMileageTransition(
mileage_trans_mat_hat
kappa_est,
num_mileage_states
);
= generatePriceTransition(
price_trans_mat_hat
lambda_est,
num_price_states
);
= Array{Float64}(
trans_mat_hat undef, num_states, num_states, num_choice
);for i in 1:num_choice
:, :, i] = kron(mileage_trans_mat_hat[:, :, i], price_trans_mat_hat);
trans_mat_hat[end
@time NFXP_opt = optimize(
-> - calculateLogLikelihoodDynamic(x, beta, trans_mat_hat, states_matrix, data_gen),
x
theta_true,Options(show_trace = false)
Optim. )
6.368038 seconds (73.23 M allocations: 6.324 GiB, 9.68% gc time, 1.16% compilation time)
* Status: success
* Candidate solution
Final objective value: 1.333722e+04
* Found with
Algorithm: Nelder-Mead
* Convergence measures
√(Σ(yᵢ-ȳ)²)/n ≤ 1.0e-08
* Work counters
Seconds run: 6 (vs limit Inf)
Iterations: 48
f(x) calls: 94
= NFXP_opt.minimizer;
theta_est
= ForwardDiff.hessian(
hessian -> - calculateLogLikelihoodDynamic(x, beta, trans_mat_hat, states_matrix, data_gen),
x
theta_est
);
= sqrt.(diag(inv(hessian))); theta_se
DataFrame(theta_est = theta_est, theta_se = theta_se)
2×2 DataFrame
Row | theta_est | theta_se |
---|---|---|
Float64 | Float64 | |
1 | 0.00401826 | 8.88336e-5 |
2 | 0.00300455 | 3.67953e-5 |
write("tmp/single_agent_dynamic_basic/data_gen.csv", data_gen);
CSV.
serialize("tmp/single_agent_dynamic_basic/kappa_est.ser", kappa_est);
serialize("tmp/single_agent_dynamic_basic/lambda_est.ser", lambda_est);
serialize("tmp/single_agent_dynamic_basic/theta_est.ser", theta_est);