File size: 3,047 Bytes
c711202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
QQ plot of z-scores against N(0,1) for UQ calibration diagnostic.

Reads z-score NPZ files saved by the scalability study.

Usage:
    julia --project=../.. plot_qq.jl results/scalability/zscores/zscores_N31.npz
    julia --project=../.. plot_qq.jl results/scalability/zscores/zscores_N31.npz results/calibrated/zscores/zscores_N31.npz --labels "Default,Calibrated"
"""

using NPZ
using CairoMakie
using Statistics
using SpecialFunctions

# Normal quantile function (inverse CDF of N(0,1))
norminv(p) = √2 * erfinv(2p - 1)

function qq_theoretical(z_empirical)
    n = length(z_empirical)
    z_sorted = sort(z_empirical)
    # Theoretical quantiles: use (i - 0.5) / n to avoid ±∞
    p = [(i - 0.5) / n for i in 1:n]
    z_theoretical = norminv.(p)
    return z_theoretical, z_sorted
end

function plot_qq(npz_paths::Vector{String}, labels::Vector{String};
                 filename::Union{Nothing,String}=nothing,
                 grid_size::Union{Nothing,Int}=nothing)

    fig = Figure(size=(800, 380), fontsize=12)

    colors = [:royalblue, :crimson, :seagreen, :darkorange]

    for (col_idx, (field, title)) in enumerate([("z_c", "Concentration"), ("z_s", "Source")])
        ax = Axis(fig[1, col_idx];
            xlabel="Theoretical quantiles (N(0,1))",
            ylabel="Empirical quantiles",
            title=title,
            aspect=1,
        )

        # Reference diagonal
        lines!(ax, [-4, 4], [-4, 4]; color=:gray60, linestyle=:dash, linewidth=1,
               label="N(0,1)")

        for (i, (path, label)) in enumerate(zip(npz_paths, labels))
            data = npzread(path)
            z = vec(data[field])
            z_th, z_emp = qq_theoretical(z)

            # Subsample for plotting if too many points
            if length(z_th) > 500
                idx = round.(Int, range(1, length(z_th), length=500))
                z_th = z_th[idx]
                z_emp = z_emp[idx]
            end

            scatter!(ax, z_th, z_emp; color=(colors[i], 0.6), markersize=3,
                     label="$label (σ̂=$(round(std(vec(data[field])), digits=2)))")
        end

        xlims!(ax, -4, 4)
        ylims!(ax, -4, 4)
        axislegend(ax; position=:lt, labelsize=10)
    end

    if !isnothing(filename)
        mkpath(dirname(filename))
        save(filename, fig, px_per_unit=3)
        println("Saved: $filename")
    end

    return fig
end

if abspath(PROGRAM_FILE) == @__FILE__
    local npz_paths = String[]
    local labels = String[]
    local output = joinpath(@__DIR__, "results", "scalability", "qq_plot.pdf")

    local idx = 1
    while idx <= length(ARGS)
        if ARGS[idx] == "--labels"
            idx += 1
            labels = String.(split(ARGS[idx], ","))
        elseif ARGS[idx] == "-o"
            idx += 1
            output = ARGS[idx]
        else
            push!(npz_paths, ARGS[idx])
        end
        idx += 1
    end

    if isempty(labels)
        labels = ["Run $j" for j in 1:length(npz_paths)]
    end

    plot_qq(npz_paths, labels; filename=output)
end