Plot Taylor diagrams

I am looking for a Julia function to create a Taylor diagram (a diagram that compares measured against estimated values for a certain variable), like in:

Please, do you have any suggestions?
Thanks in advance.

3 Likes

Hi @rio, you should already have fixed your problem, but if not, and if other people are willing to make Taylor diagrams in Julia, I can offer you the small module I made to plot the Taylor diagram. It’s made using Plots.jl “from hands” and as for now works quite well. If you have specific requests we can see what to do and obviously the repo is open for pull requests and contributions !

Have a nice day

Simon

Here is what I ported from my ggplot2 to Julia as personal learning exercise :wink: (you know).

Tried to quick add some description. Far from perfect, as you’ll see. Happy to see improvements of the plotting :-).

Btw. I don’t understand why it accepts scalar, although I type it to Array{Number}. Putting scalar causes problem with arrrows!



taylor_plot(sdmod_rel::Array{Number}, correl::Array{Number}; bias=0.0, sdObs=1.0, plotBias=false, plotVarianceErr=false)

Create an (extended) Taylorplot.

Given the model’s standard deviation relative to the observations sdmod_rel, the correlation with the
observations correl, and optionally the bias, an extended Taylor plot is created. Extended means, that apart
from the classical one (zero centered residuals, no bias) a point including the bias (indicated with an arrows
towards this point) and also a point under the scenario “= no variance error” (again with arrow) is plotted,
when plotBias and plotVarianceErr are true. sdObs is the standard deviation of the observations (it just scales the
axes essentially.)

Please note that sdmod_rel and correl need to be Arrays (otherwise arrows! does not work), so for only one point
wrap it in […].

Examples

julia> taylor_plot([0.8,1.2], [0.8, 0.5], bias=0.2, plotBias=true, plotVarianceErr=true)

function taylor_plot(sdmod_rel::Array{Number}, correl::Array{Number}; 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 = @pipe reduce(vcat, [-0.9:0.1:0.9, 0.95, 0.99]) |> setdiff(_, [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), textsize=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), textsize=10)

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

    (fig, ax)



    # taylor_grid = @pipe Base.product(correls, sds) |> DataFrame |> rename(_, [:correls, :sds])

    # iso_rmsd = @pipe Base.product(0.2:0.2:2*sdmax+0.2 |> collect, -1.0:0.01:1 |> collect) |> 
    #     DataFrame |> rename(_, [:RMSD, :xvec])

    # @chain iso_rmsd begin
    #     @rtransform! begin
    #         :x2 = (:xvec * :RMSD + 1)* sdObs
    #         :y2 = sqrt(1-:xvec^2)*:RMSD*sdObs
    #     end
    #     @rsubset! sqrt(:x2^2+:y2^2)<=1.005*maximum(taylor_grid.sds)*sdObs
    # end
    
    # @rtransform! taylor_grid :xv = :correls*:sds
    # @rtransform! taylor_grid begin
    #     :yv = sqrt(:sds^2-:xv^2)*sdObs
    #     :xv=:xv*sdObs 
    # end
    # @transform! taylor_grid begin
    # :sdsCat = categorical(:sds)
    # :correlCat = categorical(:correls)
    # end

    # #plt = data(taylor_grid) * visual(Lines, linestyle=:dash) *  mapping(:xv, :yv, group=(:correlCat, :sdsCat))
    # #plt += data(taylor_grid) * visual(Lines, linestyle=:dash) *  mapping(:xv, :yv, group=:sdsCat)
    # #draw(plt; axis=(width = 1225, height = 825))
    # fig=Figure()


   

end

You’ll get this:

P.S.: Quite proud to have posted my first maybe helpful Julia code :wink:

6 Likes