Skip to content

Commit 4142ada

Browse files
committed
make plot_irf! work with different variables
1 parent 47732d5 commit 4142ada

File tree

1 file changed

+159
-99
lines changed

1 file changed

+159
-99
lines changed

ext/StatsPlotsExt.jl

Lines changed: 159 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const irf_active_plot_container = Dict[]
99
const model_estimates_active_plot_container = Dict[]
1010

1111
import StatsPlots
12+
import DataStructures: OrderedSet
1213
import SparseArrays: SparseMatrixCSC
1314
import NLopt
1415
using DispatchDoctor
@@ -839,7 +840,7 @@ function plot_irf(𝓂::ℳ;
839840
:sylvester_algorithm => sylvester_algorithm,
840841
:lyapunov_algorithm => lyapunov_algorithm,
841842
:plot_data => Y,
842-
:reference_steady_state => reference_steady_state,
843+
:reference_steady_state => reference_steady_state[var_idx],
843844
:variable_names => variable_names,
844845
:shock_names => shock_names,
845846
:shock_idx => shock_idx,
@@ -965,35 +966,42 @@ function plot_irf_subplot(irf_data::AbstractVector{S}, steady_state::S, variable
965966
return p
966967
end
967968

968-
function plot_irf_subplot(irf_data::Vector{<:AbstractVector{S}}, steady_state::Vector{S}, variable_name::String, can_dual_axis::Bool) where S <: AbstractFloat
969-
same_ss = maximum(steady_state) - minimum(steady_state) < 1e-12
970-
969+
function plot_irf_subplot(irf_data::Vector{<:AbstractVector{S}}, steady_state::Vector{S}, variable_name::String, can_dual_axis::Bool, same_ss::Bool; pal::StatsPlots.ColorPalette = StatsPlots.palette(:auto)) where S <: AbstractFloat
971970
plot_dat = []
972971
plot_dat_dual = []
973972

974-
for i in 1:length(irf_data)
975-
if can_dual_axis && same_ss
976-
push!(plot_dat, irf_data[i] .+ steady_state[i])
977-
push!(plot_dat_dual, 100 * ((irf_data[i] .+ steady_state[i]) ./ steady_state[i] .- 1))
978-
else
979-
push!(plot_dat, irf_data[i])
973+
pal_val = Int[]
974+
975+
stst = 1.0
976+
977+
for (i,(y, ss)) in enumerate(zip(irf_data, steady_state))
978+
if !isnan(ss)
979+
if can_dual_axis && same_ss
980+
stst = ss
981+
push!(plot_dat, y .+ ss)
982+
push!(plot_dat_dual, 100 * ((y .+ ss) ./ ss .- 1))
983+
else
984+
push!(plot_dat, y)
985+
end
986+
push!(pal_val, i)
980987
end
981988
end
982989

983-
984990
p = StatsPlots.plot(plot_dat,
985991
title = variable_name,
986992
ylabel = same_ss ? "Level" : "abs. " * LaTeXStrings.L"\Delta",
993+
color = pal[pal_val]',
987994
label = "")
988995

989996
if can_dual_axis && same_ss
990997
StatsPlots.plot!(StatsPlots.twinx(),
991998
plot_dat_dual,
992999
ylabel = LaTeXStrings.L"\% \Delta",
1000+
color = pal[pal_val]',
9931001
label = "")
9941002
end
9951003

996-
StatsPlots.hline!(can_dual_axis && same_ss ? [steady_state[1] 0] : [same_ss ? steady_state[1] : 0],
1004+
StatsPlots.hline!(can_dual_axis && same_ss ? [stst 0] : [same_ss ? stst : 0],
9971005
color = :black,
9981006
label = "")
9991007

@@ -1010,7 +1018,7 @@ function plot_irf!(𝓂::ℳ;
10101018
save_plots::Bool = false,
10111019
save_plots_format::Symbol = :pdf,
10121020
save_plots_path::String = ".",
1013-
plots_per_page::Int = 9,
1021+
plots_per_page::Int = 6,
10141022
algorithm::Symbol = :first_order,
10151023
shock_size::Real = 1,
10161024
negative_shock::Bool = false,
@@ -1311,7 +1319,7 @@ function plot_irf!(𝓂::ℳ;
13111319
:sylvester_algorithm => sylvester_algorithm,
13121320
:lyapunov_algorithm => lyapunov_algorithm,
13131321
:plot_data => Y,
1314-
:reference_steady_state => reference_steady_state,
1322+
:reference_steady_state => reference_steady_state[var_idx],
13151323
:variable_names => variable_names,
13161324
:shock_names => shock_names,
13171325
:shock_idx => shock_idx,
@@ -1320,10 +1328,10 @@ function plot_irf!(𝓂::ℳ;
13201328
push!(irf_active_plot_container, args_and_kwargs)
13211329

13221330
diffdict = compare_args_and_kwargs(irf_active_plot_container)
1323-
1331+
13241332
@assert haskey(diffdict, :parameters) "New plot must be different from previous plot. Use the version without ! to plot."
13251333

1326-
param_nms = diffdict[:parameters]|>keys|>collect|>sort
1334+
param_nms = diffdict[:parameters] |> keys |> collect |> sort
13271335

13281336
annotate_ss = Vector{Pair{String, Any}}[]
13291337

@@ -1340,118 +1348,168 @@ function plot_irf!(𝓂::ℳ;
13401348
annotate_params_plot = plot_df(annotate_params)
13411349

13421350
legend_plot = StatsPlots.plot(framestyle = :none, legend_columns = length(irf_active_plot_container))
1351+
1352+
joint_shocks = OrderedSet{String}()
1353+
joint_variables = OrderedSet{String}()
13431354

13441355
for (i,k) in enumerate(irf_active_plot_container)
13451356
StatsPlots.plot!(legend_plot,
1346-
fill(0,1,1),
1347-
framestyle = :none,
1348-
legend = :inside,
1349-
label = i)
1357+
fill(0,1,1),
1358+
framestyle = :none,
1359+
legend = :inside,
1360+
label = i)
1361+
1362+
push!(joint_shocks, k[:shock_names]...)
1363+
push!(joint_variables, k[:variable_names]...)
13501364
end
13511365

1366+
sort!(joint_shocks)
1367+
sort!(joint_variables)
1368+
13521369
return_plots = []
13531370

1354-
for shock in 1:length(shock_idx)
1355-
n_subplots = length(var_idx)
1371+
for shock in joint_shocks
1372+
n_subplots = length(joint_variables)
13561373
pp = []
13571374
pane = 1
13581375
plot_count = 1
1359-
for i in 1:length(var_idx)
1360-
if all(isapprox.(Y[i,:,shock], 0, atol = eps(Float32)))
1376+
joint_non_zero_variables = []
1377+
can_dual_axiss = Bool[]
1378+
1379+
for var in joint_variables
1380+
not_zero_in_any_irf = false
1381+
can_dual_axis = gr_back
1382+
1383+
for k in irf_active_plot_container
1384+
var_idx = findfirst(==(var), k[:variable_names])
1385+
shock_idx = findfirst(==(shock), k[:shock_names])
1386+
1387+
1388+
if isnothing(var_idx) || isnothing(shock_idx)
1389+
# If the variable or shock is not present in the current irf_active_plot_container,
1390+
# we skip this iteration.
1391+
continue
1392+
else
1393+
if any(.!isapprox.(k[:plot_data][var_idx,:,shock_idx], 0, atol = eps(Float32)))
1394+
not_zero_in_any_irf = not_zero_in_any_irf || true
1395+
# break # If any irf data is not approximately zero, we set the flag to true.
1396+
end
1397+
1398+
SS = k[:reference_steady_state][var_idx]
1399+
1400+
if all((k[:plot_data][var_idx,:,shock_idx] .+ SS) .> eps(Float32)) && (SS > eps(Float32))
1401+
can_dual_axis = can_dual_axis && true
1402+
end
1403+
end
1404+
end
1405+
1406+
if not_zero_in_any_irf
1407+
push!(joint_non_zero_variables, var)
1408+
push!(can_dual_axiss, can_dual_axis)
1409+
else
1410+
# If all irf data for this variable and shock is approximately zero, we skip this subplot.
13611411
n_subplots -= 1
13621412
end
13631413
end
13641414

1365-
for i in 1:length(var_idx)
1366-
SS = reference_steady_state[var_idx[i]]
1415+
for (var, can_dual_axis) in zip(joint_non_zero_variables, can_dual_axiss)
1416+
SSs = eltype(irf_active_plot_container[1][:reference_steady_state])[]
1417+
Ys = AbstractVector{eltype(irf_active_plot_container[1][:plot_data])}[]
13671418

1368-
can_dual_axis = gr_back && all((Y[i,:,shock] .+ SS) .> eps(Float32)) && (SS > eps(Float32))
1419+
for k in irf_active_plot_container
1420+
var_idx = findfirst(==(var), k[:variable_names])
1421+
shock_idx = findfirst(==(shock), k[:shock_names])
13691422

1370-
if !(all(isapprox.(Y[i,:,shock],0,atol = eps(Float32))))
1371-
variable_name = replace_indices_in_symbol(𝓂.timings.var[var_idx[i]])
1372-
1373-
# push!(pp, plot_irf_subplot(Y[i,:,shock], SS, variable_name, can_dual_axis))
1374-
SSs = [k[:reference_steady_state][var_idx[i]] for k in irf_active_plot_container]
1375-
1376-
if maximum(SSs) - minimum(SSs) > 1e-10
1377-
push!(annotate_ss_page, String(variable_name) => minimal_sigfig_strings(SSs))
1423+
if isnothing(var_idx) || isnothing(shock_idx)
1424+
# If the variable or shock is not present in the current irf_active_plot_container,
1425+
# we skip this iteration.
1426+
push!(SSs, NaN)
1427+
push!(Ys, zeros(0))
1428+
else
1429+
push!(SSs, k[:reference_steady_state][var_idx])
1430+
push!(Ys, k[:plot_data][var_idx,:,shock_idx])
13781431
end
1432+
end
1433+
1434+
same_ss = true
13791435

1380-
push!(pp, plot_irf_subplot( [k[:plot_data][i,:,shock] for k in irf_active_plot_container],
1381-
SSs,
1382-
variable_name,
1383-
can_dual_axis))
1384-
1385-
if !(plot_count % plots_per_page == 0)
1386-
plot_count += 1
1436+
if maximum(filter(!isnan, SSs)) - minimum(filter(!isnan, SSs)) > 1e-10
1437+
push!(annotate_ss_page, var => minimal_sigfig_strings(SSs))
1438+
same_ss = false
1439+
end
1440+
1441+
push!(pp, plot_irf_subplot( Ys,
1442+
SSs,
1443+
var,
1444+
can_dual_axis,
1445+
same_ss))
1446+
1447+
if !(plot_count % plots_per_page == 0)
1448+
plot_count += 1
1449+
else
1450+
plot_count = 1
1451+
1452+
if shocks == :simulate
1453+
shock_string = ": simulate all"
1454+
shock_name = "simulation"
1455+
elseif shocks == :none
1456+
shock_string = ""
1457+
shock_name = "no_shock"
1458+
elseif shocks isa Union{Symbol_input,String_input}
1459+
shock_string = ": " * shock
1460+
shock_name = shock
13871461
else
1388-
plot_count = 1
1462+
shock_string = "Series of shocks"
1463+
shock_name = "shock_matrix"
1464+
end
13891465

1390-
if shocks == :simulate
1391-
shock_string = ": simulate all"
1392-
shock_name = "simulation"
1393-
elseif shocks == :none
1394-
shock_string = ""
1395-
shock_name = "no_shock"
1396-
elseif shocks isa Union{Symbol_input,String_input}
1397-
shock_string = ": " * replace_indices_in_symbol(𝓂.timings.exo[shock_idx[shock]])
1398-
shock_name = replace_indices_in_symbol(𝓂.timings.exo[shock_idx[shock]])
1399-
else
1400-
shock_string = "Series of shocks"
1401-
shock_name = "shock_matrix"
1402-
end
1466+
ppp = StatsPlots.plot(pp...; attributes...)
14031467

1404-
ppp = StatsPlots.plot(pp...; attributes...)
1468+
ppp_pars = StatsPlots.plot(annotate_params_plot; attributes...)
1469+
1470+
pushfirst!(annotate_ss_page, "Plot index" => 1:length(diffdict[:parameters][param_nms[1]]))
14051471

1406-
ppp_pars = StatsPlots.plot(annotate_params_plot; attributes...)
1407-
1408-
pushfirst!(annotate_ss_page, "Plot index" => 1:length(diffdict[:parameters][param_nms[1]]))
1472+
push!(annotate_ss, annotate_ss_page)
14091473

1410-
push!(annotate_ss, annotate_ss_page)
1474+
if length(annotate_ss[pane]) > 1
1475+
annotate_ss_plot = plot_df(annotate_ss[pane])
14111476

1412-
if length(annotate_ss[pane]) > 0
1413-
annotate_ss_plot = plot_df(annotate_ss[pane])
1477+
ppp_ss = StatsPlots.plot(annotate_ss_plot; attributes...)
14141478

1415-
ppp_ss = StatsPlots.plot(annotate_ss_plot; attributes...)
1416-
1417-
p = StatsPlots.plot(ppp,
1418-
legend_plot,
1419-
ppp_pars,
1420-
ppp_ss,
1421-
layout = StatsPlots.grid(4, 1, heights = [37, 1, 9, 9] ./ 56),
1422-
plot_title = "Model: "*𝓂.model_name*" " * shock_dir * shock_string *" ("*string(pane)*"/"*string(Int(ceil(n_subplots/plots_per_page)))*")";
1423-
attributes_redux...)
1424-
else
1425-
p = StatsPlots.plot(ppp,
1426-
legend_plot,
1427-
ppp_pars,
1428-
layout = StatsPlots.grid(3, 1, heights = [15, 1, 5] ./ 21),
1429-
plot_title = "Model: "*𝓂.model_name*" " * shock_dir * shock_string *" ("*string(pane)*"/"*string(Int(ceil(n_subplots/plots_per_page)))*")";
1430-
attributes_redux...)
1431-
end
1479+
p = StatsPlots.plot(ppp,
1480+
legend_plot,
1481+
ppp_pars,
1482+
ppp_ss,
1483+
layout = StatsPlots.grid(4, 1, heights = [37, 1, 9, 9] ./ 56),
1484+
plot_title = "Model: "*𝓂.model_name*" " * shock_dir * shock_string *" ("*string(pane)*"/"*string(Int(ceil(n_subplots/plots_per_page)))*")";
1485+
attributes_redux...)
1486+
else
1487+
p = StatsPlots.plot(ppp,
1488+
legend_plot,
1489+
ppp_pars,
1490+
layout = StatsPlots.grid(3, 1, heights = [15, 1, 5] ./ 21),
1491+
plot_title = "Model: "*𝓂.model_name*" " * shock_dir * shock_string *" ("*string(pane)*"/"*string(Int(ceil(n_subplots/plots_per_page)))*")";
1492+
attributes_redux...)
1493+
end
14321494

1433-
push!(return_plots,p)
1495+
push!(return_plots,p)
14341496

1435-
if show_plots
1436-
display(p)
1437-
end
1497+
if show_plots
1498+
display(p)
1499+
end
14381500

1439-
if save_plots
1440-
StatsPlots.savefig(p, save_plots_path * "/irf__" * 𝓂.model_name * "__" * shock_name * "__" * string(pane) * "." * string(save_plots_format))
1441-
end
1501+
if save_plots
1502+
StatsPlots.savefig(p, save_plots_path * "/irf__" * 𝓂.model_name * "__" * shock_name * "__" * string(pane) * "." * string(save_plots_format))
1503+
end
14421504

1443-
pane += 1
1505+
pane += 1
14441506

1445-
annotate_ss_page = Pair{String,Any}[]
1507+
annotate_ss_page = Pair{String,Any}[]
14461508

1447-
pp = []
1448-
end
1449-
end
1509+
pp = []
1510+
end
14501511
end
14511512

1452-
pushfirst!(annotate_ss_page, "Plot index" => 1:length(diffdict[:parameters][param_nms[1]]))
1453-
1454-
push!(annotate_ss, annotate_ss_page)
14551513

14561514
if length(pp) > 0
14571515
if shocks == :simulate
@@ -1461,8 +1519,8 @@ function plot_irf!(𝓂::ℳ;
14611519
shock_string = ""
14621520
shock_name = "no_shock"
14631521
elseif shocks isa Union{Symbol_input,String_input}
1464-
shock_string = ": " * replace_indices_in_symbol(𝓂.timings.exo[shock_idx[shock]])
1465-
shock_name = replace_indices_in_symbol(𝓂.timings.exo[shock_idx[shock]])
1522+
shock_string = ": " * shock
1523+
shock_name = shock
14661524
else
14671525
shock_string = "Series of shocks"
14681526
shock_name = "shock_matrix"
@@ -1476,7 +1534,7 @@ function plot_irf!(𝓂::ℳ;
14761534

14771535
push!(annotate_ss, annotate_ss_page)
14781536

1479-
if length(annotate_ss[pane]) > 0
1537+
if length(annotate_ss[pane]) > 1
14801538
annotate_ss_plot = plot_df(annotate_ss[pane])
14811539

14821540
ppp_ss = StatsPlots.plot(annotate_ss_plot; attributes...)
@@ -1584,7 +1642,9 @@ function minimal_sigfig_strings(v::AbstractVector{<:Real};
15841642
out = Vector{String}(undef, length(v))
15851643
for i in eachindex(v)
15861644
x = v[i]
1587-
if !(isfinite(x)) || x == 0
1645+
if isnan(x)
1646+
out[i] = ""
1647+
elseif !(isfinite(x)) || x == 0
15881648
# For zero or non finite just echo (rule does not change them)
15891649
out[i] = string(x)
15901650
elseif haskey(req_sig, i)

0 commit comments

Comments
 (0)