Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Taylor diagram #4771

Open
briochemc opened this issue Feb 10, 2025 · 8 comments
Open

Taylor diagram #4771

briochemc opened this issue Feb 10, 2025 · 8 comments
Labels
enhancement Feature requests and enhancements

Comments

@briochemc
Copy link
Contributor

briochemc commented Feb 10, 2025

Feature description

I would like to kindly request a Taylor diagram recipe!

For plot types, please add an image of how it should look like

Given some model data and observed (true) data, it should return a plot that looks like this (copied from the wikipedia article):

These diagrams are useful for plotting the "skill" of a model, and are used a fair bit in climate / ocean / atmospheric sciences. AFAIK, the original paper is openly accessible at https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2000JD900719.

FWIW, there is a 2-year-old Discourse comment by @mreichMPI-BGC with an implementation, but when I tried it, it threw some errors, and after applying some quick bandaid (code below), it threw a segfault for me...

# modified a tiny bit from discourse post by @mreichMPI-BGC
function taylor_plot(sdmod_rel, correl; bias=0.0, sdObs=1.0, plotBias=false, plotVarianceErr=false)
    sdmax = max(maximum(sdmod_rel), 1.0)

    ## Function to transform coordinates
    correl_sd2taylor(sd, correl) = (x=correl * sd, y=sqrt(sd^2 - (correl*sd)^2))

    ## Create Taylorplot gridlines
    correls = reduce(vcat,[-1, -0.99, -0.95, -0.9:0.1:0.9, 0.95, 0.99, 1.0])
    sds = 0:0.1:sdmax |> collect

    fig, ax, sc = scatter(0,0, color=(:black, 0));
    for c in correls
        lines!(ax, c .* sds .* sdObs * 1.02, sqrt.(sds.^2-(c .* sds).^2).*sdObs * 1.02; linestyle= :dot, color=:grey)
    end

    for s in sds
        lines!(ax, correls .* s .* sdObs, sqrt.(s.^2 .- (correls .* s).^2).*sdObs; linestyle= s==1 ? :dash : :dot, color=:grey)
    end

    ## Creat isolines of root mean squared difference (RMSD) and labels
    RMSD = 0.2:0.2:2*sdmax+0.2 |> collect
    xvec =  -1.0:0.01:1 |> collect

    rmsd_lab_x=Vector{Float64}()
    rmsd_lab_y=Vector{Float64}()
    rmsd_lab_text=Vector{String}()

    for r in RMSD[1:end-1]

        x2 = @. (xvec * r + 1) * sdObs
        y2 = @. sqrt(1-xvec^2) * r * sdObs

       i_plot = [i for i in 1:length(x2) if sqrt(x2[i]^2+y2[i]^2)<=1.005*maximum(sds)*sdObs]

      # This also works, but not if the array sent back by the compr is empty (collect does not work)
      # xp, yp = zip([(xi,yi) for (xi, yi) in zip(x2, y2) if sqrt(xi^2+yi^2)<=1.005*maximum(sds)*sdObs]...) |> collect

        lines!(ax, x2[i_plot], y2[i_plot], color=(:blue, 0.5))

        i_lab=[i for i in 1:length(x2) if (y2[i]>0.3^2*sdObs^2/abs(x2[i])) & (x2[i]>0.0) & (sqrt(x2[i]^2+y2[i]^2)<=1.005*maximum(sds)*sdObs)]
        if length(i_lab)>0
            push!(rmsd_lab_x, x2[i_lab[1]])
            push!(rmsd_lab_y, y2[i_lab[1]])
            push!(rmsd_lab_text, "$(round(r, digits=2))")
         end

    end

    #### Plotting data in Taylor space

    x,y = collect.(zip(correl_sd2taylor.(sdmod_rel, correl)...) |> collect) .* sdObs

    # First the arrows, so that the point is always on top
    if plotBias
        RMSD = @. sqrt(sdObs^2 + (sdmod_rel*sdObs)^2 - 2*sdObs^2*sdmod_rel*correl)
        RMSE = @. sqrt(bias^2+RMSD^2)
        RMSDangle = @. asin(y/RMSD)
        RMSEangle = @. asin(bias/RMSE)
        alpha = @. ifelse(x > sdObs, π - RMSDangle - RMSEangle, RMSDangle - RMSEangle)
        xb = @. sdObs - cos(alpha)* RMSE
        yb = @. sin(alpha) * RMSE
        arrows!(x, y, xb.-x, yb.-y; color=1:length(x), linewidth=3)
    end

    if plotVarianceErr
        x_no_var_err, y_no_var_err = collect.(zip(correl_sd2taylor.(1.0, correl)...) |> collect) .* sdObs
        arrows!(x,y, x_no_var_err.-x, y_no_var_err.-y; color=1:length(x), linewidth=3)
    end

    # if (plotNoVarErr) {
    #     modPointsNoVarErr <- correl_sd2Taylor(1, correl) * sdObs
    #     p  <- p + geom_point(data=modPointsNoVarErr, mapping=aes(x,y), color="red", size=2, alpha=0.5)

    ## Plot data points (original Taylorplot)
    scatter!(ax, x, y, color=1:length(x),  strokewidth=0.5, glowwidth = 5.0, glowcolor=(:black,0.5) )

    ## Plot point of "perfect model"
    scatter!(ax, sdObs, 0.0, markersize=20, color=(:blue,0.5))



    ### Labels for coordinate system
    correlTicks = setdiff(reduce(vcat, [-0.9:0.1:0.9, 0.95, 0.99]), [0])
    text!(correlTicks*sdmax*sdObs*1.04, sqrt.(sdmax^2 .- (correlTicks*sdmax).^2)*sdObs*1.04,
        text=["$(round(c, digits=2))" for c in correlTicks], align=(:center,:center), fontsize=10)
    #scatter!(correlTicks*sdmax*sdObs*1.04, sqrt.(sdmax^2 .- (correlTicks*sdmax).^2)*sdObs*1.04)
    scatter!(rmsd_lab_x,rmsd_lab_y, markersize=(30,20), color=:white, strokewidth=0.5)
    text!(rmsd_lab_x,rmsd_lab_y, text=rmsd_lab_text, align=(:center,:center), fontsize=10)

    ## Clipping plot
    xlims!(ax, min(0, 1.1*minimum(x)), max(sdObs,1.1*maximum(x)))

    (fig, ax)

end

fig, ax = taylor_plot([0.8,1.2], [0.8, 0.5], bias=0.2, plotBias=true, plotVarianceErr=true)
@briochemc briochemc added the enhancement Feature requests and enhancements label Feb 10, 2025
@asinghvi17
Copy link
Member

asinghvi17 commented Feb 10, 2025

Segfaults definitely shouldn't be happening...could you unpack the function (or just insert display(fig) after every plotting call) and see where it drops out?

Image

I tried to adapt this to a polar axis:

# Taylor plot

# INPUT
sdmod_rel = [0.8,1.2]
correl = [0.8, 0.5] 
bias=0.2
sd_obs = 1.0
plot_bias = true
plot_variance_error = true

# Do the actual plotting now
# First, construct the figure and a polar axis on the first quadrant
fig = Figure()
ax = PolarAxis(fig[1, 1])
ax.thetalimits[] = (0, pi/2) # first quadrant only

# Now, style the axis appropriately for a Taylor plot
ax.thetagridcolor = (:black, 0.1)
ax.thetagridstyle = :dash

# Now, go forth and plot!

# First, plot the grid lines for the correlation
# TODO: it would be better if we had labelled lines
# plucked from the contour recipe,
# but since we don't, we can do this manually.

"A transformation function that goes from correlation and standard deviation to the Taylor plot's Cartesian space."
correl_sd2taylor_transf = Makie.PointTrans{2}() do (correl, sd)
    Point2(correl * sd, sqrt(sd^2-(correl*sd)^2)) * sd_obs
end
correl_sd2taylor(sd, correl) = (x=correl * sd, y=sqrt(sd^2-(correl*sd)^2))

## Create Taylorplot gridlines
# sdmax = max(maximum(sdmod_rel), 1.0)
# correls = reduce(vcat,[-1, -0.99, -0.95, -0.9:0.1:0.9, 0.95, 0.99, 1.0])
# sds = 0:0.1:sdmax |> collect

# Create isolines of root mean squared difference (RMSD) and labels
RMSD = 0.2:0.2:2*sdmax+0.2
xvec = -1.0:0.01:1 

for r in RMSD[1:end-1]
    # this is slightly different from the "true transform"
    # so we compute the points directly and plot in corrected space
    points = @. Point2f((xvec * r + 1) * sd_obs, sqrt(1-xvec^2) * r * sd_obs)
    points_inds = splat(hypot).(points) .<= 1.005 * (maximum(sds)) * sd_obs

    lines!(ax, 
        points[points_inds];
        color=(:blue, 0.5),
        transformation = Transformation(ax.scene.transformation; transform_func = identity),
        xautolimits = false,
        yautolimits = false,
        # label = "RMSD $r"
    ) # each line gets a label, so they should be separate

end

# Now, plot the actual data

# First the arrows

xy_orig = Point2f.(sdmod_rel, correl)
x,y = collect.(zip(correl_sd2taylor.(sdmod_rel, correl)...) |> collect) .* sd_obs
xy = Point2f.(x,y)

if plot_bias
    # here we want to be in Cartesian space, since we're calculating angles
    RMSD = @. sqrt(sd_obs^2 + (sdmod_rel*sd_obs)^2 - 2*sd_obs^2*sdmod_rel*correl)
    RMSE = @. sqrt(bias^2+RMSD^2)
    RMSDangle = @. atan(y/RMSD)
    RMSEangle = @. atan(bias/RMSE)
    alpha = @. ifelse(x > sd_obs, π - RMSDangle - RMSEangle, RMSDangle - RMSEangle)
    xb = @. sd_obs - cos(alpha)* RMSE
    yb = @. sin(alpha) * RMSE
    arrows!(ax, x, y, xb.-x, yb.-y; color=1:length(x), linewidth=3, transformation = Transformation(ax.scene.transformation; transform_func = identity))
end


if plot_variance_error
    # this should also be in Cartesian space because the vector subtraction has to happen there.
    xy_no_var_err = Point2f.(1.0, correl)
    xy_no_var_err = Makie.apply_transform(correl_sd2taylor_transf, xy_no_var_err)
    arrows!(ax, xy, xy_no_var_err .- xy; color=1:length(xy), linewidth=3, transformation = Transformation(ax.scene.transformation; transform_func = identity))
end

fig

but the arrows seem wrong...maybe there's an issue with how I am going from one space to another?

Ideally one has some kind of TaylorAxis maybe that does all this, or a taylorplot recipe which returns a SpecApi polar axis!

@briochemc
Copy link
Contributor Author

briochemc commented Feb 10, 2025

🤔 I was trying your code and I am getting a segfault again (GLMakie v0.10.18):

julia> for r in RMSD[1:end-1]
           # this is slightly different from the "true transform"
           # so we compute the points directly and plot in corrected space
           points = @. Point2f((xvec * r + 1) * sd_obs, sqrt(1-xvec^2) * r * sd_obs)
           points_inds = splat(hypot).(points) .<= 1.005 * (maximum(sds)) * sd_obs

           lines!(ax,
               points[points_inds];
               color=(:blue, 0.5),
               transformation = Transformation(ax.scene.transformation; transform_func = identity),
               xautolimits = false,
               yautolimits = false,
               # label = "RMSD $r"
           ) # each line gets a label, so they should be separate

       end

       # Now, plot the actual data

       # First the arrows


[49476] signal 11 (1): Segmentation fault: 11
in expression starting at none:0
gleRunVertexSubmitImmediate at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
gleDrawArraysOrElements_ExecCore at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
glDrawElements_GL3Exec at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
glDrawElements at /Users/benoitpasquier/.julia/packages/ModernGL/BUvna/src/functionloading.jl:73 [inlined]
render at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/GLAbstraction/GLRender.jl:132
jfptr_render_60756 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
StandardPostrender at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/GLAbstraction/GLRenderObject.jl:68
jfptr_StandardPostrender_65527 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/GLAbstraction/GLRender.jl:92
jfptr_render_60825 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/GLAbstraction/GLRender.jl:70
jfptr_render_60772 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/rendering.jl:127
#render_frame#158 at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/rendering.jl:75
render_frame at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/rendering.jl:29 [inlined]
on_demand_renderloop at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/screen.jl:939
renderloop at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/screen.jl:963
jfptr_renderloop_61300 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
#71 at /Users/benoitpasquier/.julia/packages/GLMakie/TH3rf/src/screen.jl:823
jfptr_YY.71_61597 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
jl_apply at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZM05NJYVY.0/build/default-grannysmith-C07ZM05NJYVY-0/julialang/julia-release-1-dot-11/src/./julia.h:2157 [inlined]
start_task at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZM05NJYVY.0/build/default-grannysmith-C07ZM05NJYVY-0/julialang/julia-release-1-dot-11/src/task.c:1202
Allocations: 18659365 (Pool: 18649180; Big: 10185); GC: 15
Segmentation fault: 11

EDIT: I'll update GLMakie and try again. BTW unless it's just a coincidence, it seems I'm not the only one (just saw some similar segafault on the slack Makie channel).

EDIT2: Yep, same segfault with GLMakie v0.11.2, Makie v0.22.1.

julia> versioninfo()
Julia Version 1.11.3
Commit d63adeda50d (2025-01-21 19:42 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: macOS (x86_64-apple-darwin24.0.0)
  CPU: 12 × Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, skylake)
Threads: 1 default, 0 interactive, 1 GC (on 12 virtual cores)

@asinghvi17
Copy link
Member

asinghvi17 commented Feb 10, 2025

Huh, looks like segfaults moved from m1 (I used to see a bunch, but not in the same place that you are seeing them and not anymore) on to Intel :D .

@briochemc I would move to WGLMakie or CairoMakie for now - for this kind of diagram, it won't make a difference.

@briochemc
Copy link
Contributor Author

briochemc commented Feb 11, 2025

I'll admit I don't really understand the arrows yet, but testing with some other data taken from that python example I get the right locations of the dots:

Julia Python reference

code (not cleaned up):

# Taylor plot
using CairoMakie
using Statistics

# model data is a vector (list) of N-dimensional arrays
# copied values from https://easyclimate.readthedocs.io/en/latest/auto_gallery_output/plot_taylor_diagram.html#sphx-glr-download-auto-gallery-output-plot-taylor-diagram-py
model_data = [
    [1  , 2  , 3  ,  0.1,  0.2, 0.3, 3.2, 0.6, 1.8],
    [0.2, 0.4, 0.6, 15  , 10  , 5  , 3.2, 0.6, 1.8]
]
obs_data = sum(model_data) / 1.85


# INPUT
# sdmod_rel = [0.8,1.2]
sdmod_rel = [std(data) ./ std(obs_data) for data in model_data]
# sdmod_rel = [std(data) for data in model_data]
# correl = [0.8, 0.5]
correl = [cor(data, obs_data) for data in model_data]
bias = 0.3
sd_obs = 1.0
plot_bias = false
plot_variance_error = false

# Do the actual plotting now
# First, construct the figure and a polar axis on the first quadrant
fig = Figure(size=(400, 400))

# Corrticks for Taylor diagram
# corrticks = [-1; -0.99; -0.95; -0.9:0.1:0.9; 0.95; 0.99; 1.0]
# corrminorticks = [-1; -0.99:0.01:-0.9; 0.85:0.05:0.85; 0.9:0.01:0.99; 1.0]
corrticks = [-1; -0.99; -0.95; -0.9:0.1:-0.7; -0.6:0.2:0.6; 0.7:0.1:0.9; 0.95; 0.99; 1.0]
# corrminorticks = [-1; -0.99:0.01:-0.9; 0.85:0.05:0.85; 0.9:0.01:0.99; 1.0]
# thetaticks for taylor diagram
thetaticks = (acos.(corrticks), string.(corrticks))
# thetaminorticks = acos.(corrminorticks)
rlimits = (0, 2.7)

ax = PolarAxis(fig[1, 1];
    thetalimits = (0, π), # first quadrant only
    thetagridcolor = (:black, 0.5),
    thetagridstyle = :dot,
    thetaticks,
    # thetaminorticks,
    rlimits,
    rgridcolor = cgrad(:Archambault, categorical = true)[1],
    rticklabelcolor = cgrad(:Archambault, categorical = true)[1],
    rgridstyle = :dash,
    rticks = -2.5:0.5:2.5,
)
# # ax.thetalimits[] = (0, pi/2) # first quadrant only
# ax.thetalimits[] = (0, pi) # first quadrant only

# # Now, style the axis appropriately for a Taylor plot
# ax.thetagridcolor = (:black, 0.1)
# ax.thetagridstyle = :dash

# Now, go forth and plot!

# First, plot the grid lines for the correlation
# TODO: it would be better if we had labelled lines
# plucked from the contour recipe,
# but since we don't, we can do this manually.

"A transformation function that goes from correlation and standard deviation to the Taylor plot's Cartesian space."
correl_sd2taylor_transf = Makie.PointTrans{2}() do (correl, sd)
    Point2(correl * sd, sqrt(sd^2-(correl*sd)^2)) * sd_obs
end
correl_sd2taylor(sd, correl) = (x=correl * sd, y=sqrt(sd^2-(correl*sd)^2))

# Create Taylorplot gridlines
# sdmax = max(maximum(sdmod_rel), 1.0)
sdmax = 2rlimits[2]
correls = reduce(vcat,[-1, -0.99, -0.95, -0.9:0.1:0.9, 0.95, 0.99, 1.0])
sds = 0:0.1:sdmax |> collect

# Create isolines of root mean squared difference (RMSD) and labels
# RMSD = 0.2:0.2:2*sdmax+0.2
RMSD = 0.5:0.5:2*sdmax+0.5
xvec = -1.0:0.01:1

for r in RMSD[1:end-1]
    # this is slightly different from the "true transform"
    # so we compute the points directly and plot in corrected space
    points = @. Point2f((xvec * r + 1) * sd_obs, sqrt(1-xvec^2) * r * sd_obs)
    points_inds = splat(hypot).(points) .<= 1.005 * (maximum(sds)) * sd_obs

    lines!(ax,
        points[points_inds];
        # color=(:black, 0.5),
        color = cgrad(:Archambault, categorical = true)[3],
        linewidth = 1,
        transformation = Transformation(ax.scene.transformation; transform_func = identity),
        xautolimits = false,
        yautolimits = false,
        # label = "RMSD $r"
    ) # each line gets a label, so they should be separate

end

# Now, plot the actual data

# First the arrows

xy_orig = Point2f.(sdmod_rel, correl)
x,y = collect.(zip(correl_sd2taylor.(sdmod_rel, correl)...) |> collect) .* sd_obs
xy = Point2f.(x,y)

scatter!(ax, x, y;
    color = [:red, :green],
    marker = [:cross, :star5],
    transformation = Transformation(ax.scene.transformation; transform_func = identity)
)
scatter!(ax, [1], [0];
    color = :black,
    transformation = Transformation(ax.scene.transformation; transform_func = identity)
)


if plot_bias
    # here we want to be in Cartesian space, since we're calculating angles
    RMSD = @. sqrt(sd_obs^2 + (sdmod_rel*sd_obs)^2 - 2*sd_obs^2*sdmod_rel*correl)
    RMSE = @. sqrt(bias^2+RMSD^2)
    RMSDangle = @. atan(y/RMSD)
    RMSEangle = @. atan(bias/RMSE)
    alpha = @. ifelse(x > sd_obs, π - RMSDangle - RMSEangle, RMSDangle - RMSEangle)
    xb = @. sd_obs - cos(alpha)* RMSE
    yb = @. sin(alpha) * RMSE
    arrows!(ax, x, y, xb.-x, yb.-y; color=1:length(x), linewidth=3, transformation = Transformation(ax.scene.transformation; transform_func = identity))
end


if plot_variance_error
    # this should also be in Cartesian space because the vector subtraction has to happen there.
    xy_no_var_err = Point2f.(1.0, correl)
    xy_no_var_err = Makie.apply_transform(correl_sd2taylor_transf, xy_no_var_err)
    arrows!(ax, xy, xy_no_var_err .- xy; color=1:length(xy), linewidth=3, transformation = Transformation(ax.scene.transformation; transform_func = identity))
end

fig

EDIT: Actually maybe it's a bit off 🤔

This was referenced Feb 11, 2025
@briochemc
Copy link
Contributor Author

briochemc commented Feb 11, 2025

@briochemc
Copy link
Contributor Author

briochemc commented Feb 11, 2025

I ended up baking my own based on your code @asinghvi17 (but I used contour and its labels directly), giving

Image
# Taylor plot
using CairoMakie
using Statistics

function taylordiagramvalues(f, r, args...)

    # STDs and means
    σf = std(f, args...; corrected = false)
    σr = std(r, args...; corrected = false)
    f̄ = mean(f, args...)
    r̄ = mean(r, args...)

    # Correlation coefficient
    R = cor(f, r, args...)

    # Root Mean Square Difference
    E = sqrt(mean((f .- r) .^ 2, args...))

    # Bias=-# Centered Root Mean Square Difference
    E′ = sqrt(mean(((f .- f̄) - (r .- r̄)) .^ 2, args...))

    # Full Mean Square Difference= E′^2 +^2

    # Normalized values (maybe that needs to be a kwarg)
    Ê′ = E′ / σr
    σ̂f = σf / σr
    σ̂r = 1.0

    return (; σr, σf, R, E, Ē, E′, E², Ê′, σ̂f, σ̂r)
end

# model data is a vector (list) of N-dimensional arrays
# copied values from https://easyclimate.readthedocs.io/en/latest/auto_gallery_output/plot_taylor_diagram.html#sphx-glr-download-auto-gallery-output-plot-taylor-diagram-py
model_data = [
    [1  , 2  , 3  ,  0.1,  0.2, 0.3, 3.2, 0.6, 1.8],
    [0.2, 0.4, 0.6, 15  , 10  , 5  , 3.2, 0.6, 1.8]
]
obs_data = sum(model_data) / 1.85

# Calculate the Taylor diagram values
TDvals = [taylordiagramvalues(data, obs_data) for data in model_data]
σr = TDvals[1].σr
σmax = 2.7TDvals[1].σr

# Do the actual plotting now
# First, construct the figure and a polar axis on the first quadrant
fig = Figure(size=(600, 600))

# Corrticks for Taylor diagram
corrticks = [-1; -0.99; -0.95; -0.9:0.1:-0.7; -0.6:0.2:0.6; 0.7:0.1:0.9; 0.95; 0.99; 1.0]

ax = PolarAxis(fig[1, 1];
    thetalimits = (0, π), # first quadrant only
    thetagridcolor = (:black, 0.5),
    thetagridstyle = :dot,
    thetaticks = (acos.(corrticks), string.(corrticks)),
    # thetaminorticks,
    rlimits = (0, σmax),
    rgridcolor = cgrad(:Archambault, categorical = true)[1],
    rticklabelcolor = cgrad(:Archambault, categorical = true)[1],
    rgridstyle = :dash,
    rticks = 0:1:σmax,
)

# Create isolines of root centered mean squared difference (E′) and labels
levels = (0.5:0.5:4) .* σr
rgrid = (0:0.01:1) .* σmax
θgrid = (0:0.01:π)
E′grid = [sqrt(σr^2 + r^2 - 2 * σr * r * cos(θ)) for θ in θgrid, r in rgrid]
contour!(ax, θgrid, rgrid, E′grid;
    levels,
    labels = true,
    color = cgrad(:Archambault, categorical = true)[3]
)

# Now, plot the actual data
σfs = [vals.σf for vals in TDvals]
Rs = [vals.R for vals in TDvals]

# Transform data to Cartesian space?
"A transformation function that goes from correlation and standard deviation to the Taylor plot's Cartesian space."
xy_from_R_and_σ(R, σ) = Point2* R, sqrt^2 -* R)^2))
x, y = collect.(zip(xy_from_R_and_σ.([Rs; 1], [σfs; σr])...) |> collect)
# Above I used R = 1 and σr for the reference point

scatter!(ax, x, y;
    color = [:red, :green, :black],
    marker = [:cross, :star5, :circle],
    transformation = Transformation(ax.scene.transformation; transform_func = identity)
)

fig

Note that I used the original paper's notation for clarity. I also left some args... in there because I could use some weights as well. Also note that my correlation tick labels could be improved with a proper minus sign. But overall I'm pretty happy with that and it's not much code TBH. The only thing I would like is ticks (#4773) and axis labels (#4076).

@SimonDanisch
Copy link
Member

Can anyone make a simple 2-5 line MWE, so we can investigate the segfault?

@briochemc
Copy link
Contributor Author

briochemc commented Feb 11, 2025

Can anyone make a simple 2-5 line MWE, so we can investigate the segfault?

@SimonDanisch For me this minimal example:

using GLMakie
fig, ax, plt = lines(Point{2, Float32}[[-1.2, 0.0]])

throws this segfault:

[45304] signal 11 (1): Segmentation fault: 11
in expression starting at none:0
gleRunVertexSubmitImmediate at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
gleDrawArraysOrElements_ExecCore at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
glDrawElements_GL3Exec at /System/Library/Frameworks/OpenGL.framework/Versions/A/Resources/GLEngine.bundle/GLEngine (unknown line)
glDrawElements at /Users/benoitpasquier/.julia/packages/ModernGL/BUvna/src/functionloading.jl:73 [inlined]
render at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/GLAbstraction/GLRender.jl:137
jfptr_render_59155 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
StandardPostrender at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/GLAbstraction/GLRenderObject.jl:68
jfptr_StandardPostrender_64033 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/GLAbstraction/GLRender.jl:93
jfptr_render_59124 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/GLAbstraction/GLRender.jl:71
jfptr_render_59084 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
render at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/rendering.jl:132
#render_frame#160 at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/rendering.jl:78
render_frame at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/rendering.jl:29 [inlined]
on_demand_renderloop at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/screen.jl:1031
renderloop at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/screen.jl:1055
jfptr_renderloop_59838 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
#79 at /Users/benoitpasquier/.julia/packages/GLMakie/fj8mE/src/screen.jl:916
jfptr_YY.79_60179 at /Users/benoitpasquier/.julia/compiled/v1.11/GLMakie/nfnZR_l4n56.dylib (unknown line)
jl_apply at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZM05NJYVY.0/build/default-grannysmith-C07ZM05NJYVY-0/julialang/julia-release-1-dot-11/src/./julia.h:2157 [inlined]
start_task at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZM05NJYVY.0/build/default-grannysmith-C07ZM05NJYVY-0/julialang/julia-release-1-dot-11/src/task.c:1202
Allocations: 11313717 (Pool: 11303570; Big: 10147); GC: 11
Segmentation fault: 11

Some setup details:

(@v1.11) pkg> st -m GLMakie Makie
Status `~/.julia/environments/v1.11/Manifest.toml`
  [e9467ef8] GLMakie v0.11.2
  [ee78f7c6] Makie v0.22.1

julia> versioninfo()
Julia Version 1.11.3
Commit d63adeda50d (2025-01-21 19:42 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: macOS (x86_64-apple-darwin24.0.0)
  CPU: 12 × Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, skylake)
Threads: 1 default, 0 interactive, 1 GC (on 12 virtual cores)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Feature requests and enhancements
Projects
None yet
Development

No branches or pull requests

3 participants