From cb921565fac008c81eec627c683bd9f1920e8a1d Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 15 May 2024 08:50:03 +1000 Subject: [PATCH 01/23] Add file to `analysis` for DEA and cost functions --- src/analysis/DEA.jl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/analysis/DEA.jl diff --git a/src/analysis/DEA.jl b/src/analysis/DEA.jl new file mode 100644 index 000000000..e74d7a90a --- /dev/null +++ b/src/analysis/DEA.jl @@ -0,0 +1,28 @@ +using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA +using DataFrames, YAXARrays +using StableRNGs + +function deployment_cost(rs::ResultSet)::YAXArray + scenarios = rs.inputs + site_data = rs.site_data + + # Get total cost for no. of corals deployed in each scenario + prod_cost = prodcution_cost(scenarios, site_data) + # Get logistics cost for corals deployed in each scenarios + logistics_cost = logistics_cost(scenarios, site_data) + + return YAXArray((Dim{scenarios}(1:size(scenarios, 1)),), prod_cost .+ logistics_cost) +end +function data_envelopment_analysis( + rs::ResultSet, metrics...; dea_function::Function=dea, rts::Symbol=:VRS, + orient::Symbol=:Output +)::Tuple{AbstractDEAModel,AbstractArray} + cost = deployment_cost(rs) + X = metrics[keys(metrics)[1]] + for metric in keys(metrics)[2:end] + vcat!(X, Array(metrics[metric])) + end + + result = dea_function(Array(cost), X; orient=orient, rts=rts) + return result, X +end From 7d37b412f35a6d6557e4e4d18fcb5656a722f6ff Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 15 May 2024 10:06:30 +1000 Subject: [PATCH 02/23] Split into 2 functions to allow array or multiple metrics as tuple to be input Change default DEA model to `deabigdata` (deals with large data sets more efficiently. Rename file and remove StableRNGs import as not needed --- src/analysis/{DEA.jl => economics.jl} | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) rename src/analysis/{DEA.jl => economics.jl} (63%) diff --git a/src/analysis/DEA.jl b/src/analysis/economics.jl similarity index 63% rename from src/analysis/DEA.jl rename to src/analysis/economics.jl index e74d7a90a..b7e9b28a4 100644 --- a/src/analysis/DEA.jl +++ b/src/analysis/economics.jl @@ -1,6 +1,5 @@ using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA using DataFrames, YAXARrays -using StableRNGs function deployment_cost(rs::ResultSet)::YAXArray scenarios = rs.inputs @@ -14,15 +13,21 @@ function deployment_cost(rs::ResultSet)::YAXArray return YAXArray((Dim{scenarios}(1:size(scenarios, 1)),), prod_cost .+ logistics_cost) end function data_envelopment_analysis( - rs::ResultSet, metrics...; dea_function::Function=dea, rts::Symbol=:VRS, - orient::Symbol=:Output + rs::ResultSet, metrics...; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=deabigdata )::Tuple{AbstractDEAModel,AbstractArray} - cost = deployment_cost(rs) X = metrics[keys(metrics)[1]] for metric in keys(metrics)[2:end] vcat!(X, Array(metrics[metric])) end - result = dea_function(Array(cost), X; orient=orient, rts=rts) + return data_envelopment_analysis(rs, X; rts=rts, orient=orient, dea_model=dea_model) +end +function data_envelopment_analysis( + rs::ResultSet, X::Array{Float64}; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=deabigdata +)::Tuple{AbstractDEAModel,AbstractArray} + cost = deployment_cost(rs) + result = dea_model(Array(cost), X; orient=orient, rts=rts) return result, X end From 40fae646473378e300eca3e3e1b82654e7d3b59a Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 15 May 2024 10:07:00 +1000 Subject: [PATCH 03/23] Add preliminary functions for plotting the DEA best practice frontier --- ext/AvizExt/viz/economics.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 ext/AvizExt/viz/economics.jl diff --git a/ext/AvizExt/viz/economics.jl b/ext/AvizExt/viz/economics.jl new file mode 100644 index 000000000..cb5f786fb --- /dev/null +++ b/ext/AvizExt/viz/economics.jl @@ -0,0 +1,36 @@ + +function ADRIA.viz.data_envelopment_analysis( + rs::ResultSet, X::Array{Float64}, efficiencies::Array{Float64}; axis_opts=Dict(), + opts=Dict() +) + f = Figure() + g = Axis(f[1, 1]; axis_opts...) + return ADRIA.viz.data_envelopment_analysis(g, rs, X, efficiencies; opts=opts) +end +function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, + rs::ResultSet, X::Array{Float64}, efficiencies::Array{Float64}; opts=Dict() +) + return ADRIA.viz.data_envelopment_analysis( + g, X[1, :], X[2, :], efficiencies; opts=opts + ) +end +function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, + metric_1::AbstractArray, metric_2::AbstractArray, + efficiencies::Array; opts=Dict()) + line_color = get(opts, :line_color, :red) + data_color = get(opts, :data_color, :black) + frontier_name = get(opts, :frontier_name, "Best practice frontier") + data_name = get(opts, :data_name, "Scenario data cloud") + + # Find points on best practice frontier + best_practice_scens = findall(efficiencies .== 1.0) + + # Plot frontier and data cloud + frontier = lines!( + g, metric_1[best_practice_scens], metric_2[best_practice_scens]; color=line_color + ) + data = scatter!(g, metric_1, metric_2; color=data_color) + Legend(g, [frontier, data], [frontier_name, data_name]) + + return g +end From 8dd9431f0c18721b390f296122a03d4c3a6f162e Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 15 May 2024 10:28:11 +1000 Subject: [PATCH 04/23] Change input types and names of variables for DEA plotting to improve clarity Fix typo Add preliminary version of coral deployment cost function Add DataEnvelopmentAnalysis and BasicInterpolators to ADRIA package Add `economics.jl" to analysis.jl imports Add cost data for interpolators Add basic DEA functions for ADRIA Add first draft of CAD cost function Make `economics` a module --- Project.toml | 2 + ext/AvizExt/viz/economics.jl | 18 +++++---- src/ADRIA.jl | 1 + src/analysis/deploy_cap_cost.csv | 2 + src/analysis/deploy_op_cost.csv | 5 +++ src/analysis/economics.jl | 64 +++++++++++++++++++------------- 6 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 src/analysis/deploy_cap_cost.csv create mode 100644 src/analysis/deploy_op_cost.csv diff --git a/Project.toml b/Project.toml index 69edb153c..d005a4098 100644 --- a/Project.toml +++ b/Project.toml @@ -5,12 +5,14 @@ version = "0.11.0" [deps] ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3" +BlackBoxOptim = "a134a8b2-14d6-55f6-9291-3336d3ab0209" Bootstrap = "e28b5b4c-05e8-5b66-bc03-6f0c0a0a06e0" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" CoralBlox = "f00a6e10-004a-11ef-3676-67ec635bc1a2" CpuId = "adafc99b-e345-5852-983c-f28acb93d879" +DataEnvelopmentAnalysis = "a100299e-89d6-11e9-0fa0-2daf497e6a05" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/ext/AvizExt/viz/economics.jl b/ext/AvizExt/viz/economics.jl index cb5f786fb..3832590f1 100644 --- a/ext/AvizExt/viz/economics.jl +++ b/ext/AvizExt/viz/economics.jl @@ -1,22 +1,24 @@ function ADRIA.viz.data_envelopment_analysis( - rs::ResultSet, X::Array{Float64}, efficiencies::Array{Float64}; axis_opts=Dict(), + rs::ResultSet, costs::Vector{Float64}, X::Vector{Float64}, efficiencies::Array{Float64}; + axis_opts=Dict(), opts=Dict() ) f = Figure() g = Axis(f[1, 1]; axis_opts...) - return ADRIA.viz.data_envelopment_analysis(g, rs, X, efficiencies; opts=opts) + return ADRIA.viz.data_envelopment_analysis(g, rs, costs, X, efficiencies; opts=opts) end function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - rs::ResultSet, X::Array{Float64}, efficiencies::Array{Float64}; opts=Dict() + rs::ResultSet, costs::Vector{Float64}, X::Vector{Float64}, efficiencies::Array{Float64}; + opts=Dict() ) return ADRIA.viz.data_envelopment_analysis( - g, X[1, :], X[2, :], efficiencies; opts=opts + g, costs, X, efficiencies; opts=opts ) end function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - metric_1::AbstractArray, metric_2::AbstractArray, - efficiencies::Array; opts=Dict()) + Y::Vector{Float64}, X::Vector{Float64}, + efficiencies::Vector{Float64}; opts=Dict()) line_color = get(opts, :line_color, :red) data_color = get(opts, :data_color, :black) frontier_name = get(opts, :frontier_name, "Best practice frontier") @@ -27,9 +29,9 @@ function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, # Plot frontier and data cloud frontier = lines!( - g, metric_1[best_practice_scens], metric_2[best_practice_scens]; color=line_color + g, Y[best_practice_scens], X[best_practice_scens]; color=line_color ) - data = scatter!(g, metric_1, metric_2; color=data_color) + data = scatter!(g, Y, X; color=data_color) Legend(g, [frontier, data], [frontier_name, data_name]) return g diff --git a/src/ADRIA.jl b/src/ADRIA.jl index fb42809fb..2e3066abc 100644 --- a/src/ADRIA.jl +++ b/src/ADRIA.jl @@ -77,6 +77,7 @@ include("metrics/performance.jl") include("scenario.jl") include("analysis/analysis.jl") include("analysis/sensitivity.jl") +include("analysis/economics.jl") include("ExtInterface/ADRIA/Domain.jl") include("ExtInterface/ReefMod/RMEDomain.jl") diff --git a/src/analysis/deploy_cap_cost.csv b/src/analysis/deploy_cap_cost.csv new file mode 100644 index 000000000..68844656d --- /dev/null +++ b/src/analysis/deploy_cap_cost.csv @@ -0,0 +1,2 @@ +100,500,1000,5000,10000,50000,90000,200000,250000,390000,450000,500000,600000,800000,1000000,2000000,4000000,6000000,8000000,10000000 +177470,177470,177470,177470,177470,177470,185470,193470,201470,378940,386940,394940,402940,418940,612410,1047350,2094700,3142050,4189400,5067280 diff --git a/src/analysis/deploy_op_cost.csv b/src/analysis/deploy_op_cost.csv new file mode 100644 index 000000000..4bdca6853 --- /dev/null +++ b/src/analysis/deploy_op_cost.csv @@ -0,0 +1,5 @@ +Reef/No. of Devices,100,500,1000,5000,10000,50000,100000,500000,1000000,5000000,10000000 +Moore,387.97,77.59,38.8,13.82,7.26,3.08,2.69,2.59,2.55,2.54,2.53 +Davies,631.97,126.39,63.2,12.99,9.7,3.57,3.22,2.9,2.87,2.83,2.82 +Swains,1319.97,263.99,132,26.75,16.58,4.95,4.88,3.89,3.87,2.32,3.85 +Keppel,462.97,92.59,46.3,9.61,5.15,3.23,2.77,2.46,2.42,2.4,2.39 diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index b7e9b28a4..0697c5c15 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -1,33 +1,47 @@ -using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA -using DataFrames, YAXARrays - -function deployment_cost(rs::ResultSet)::YAXArray - scenarios = rs.inputs - site_data = rs.site_data +module economics - # Get total cost for no. of corals deployed in each scenario - prod_cost = prodcution_cost(scenarios, site_data) - # Get logistics cost for corals deployed in each scenarios - logistics_cost = logistics_cost(scenarios, site_data) +using CSV +using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA +using BasicInterpolators +using ADRIA: ResultSet +using DataFrames, YAXArrays - return YAXArray((Dim{scenarios}(1:size(scenarios, 1)),), prod_cost .+ logistics_cost) -end function data_envelopment_analysis( - rs::ResultSet, metrics...; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=deabigdata -)::Tuple{AbstractDEAModel,AbstractArray} - X = metrics[keys(metrics)[1]] - for metric in keys(metrics)[2:end] - vcat!(X, Array(metrics[metric])) - end - - return data_envelopment_analysis(rs, X; rts=rts, orient=orient, dea_model=dea_model) + rs::ResultSet, cost::Vector{Float64}, metrics...; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +)::Tuple{DEA.AbstractDEAModel,AbstractArray} + X = Array(hcat(metrics...)) + return data_envelopment_analysis( + rs, cost, X; rts=rts, orient=orient, dea_model=dea_model + ) end function data_envelopment_analysis( - rs::ResultSet, X::Array{Float64}; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=deabigdata -)::Tuple{AbstractDEAModel,AbstractArray} - cost = deployment_cost(rs) + rs::ResultSet, cost::Vector{Float64}, X::Matrix{Float64}; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +)::Tuple{DEA.AbstractDEAModel,AbstractArray} result = dea_model(Array(cost), X; orient=orient, rts=rts) return result, X end + +function CAD_cost(scenarios::DataFrame; Reef::String="Moore") + # No. of corals deployed in each scenario + scen_no_corals = + scenarios[:, :N_seed_CA] .+ scenarios[:, :N_seed_SM] .+ scenarios[:, :N_seed_TA] + + scen_no_years = scenarios[:, :seed_years] + deploy_op_cost = CSV.read("deploy_op_cost.csv", DataFrame; header=false) + deploy_cap_cost = CSV.read("deploy_cap_cost.csv", DataFrame; header=false) + reef_ind = findfirst(deploy_op_cost[2:end, 1] .== Reef) + + OP_lin = LinearInterpolator( + Array(deploy_op_cost[1, 2:end]), Array(deploy_op_cost[reef_ind, 2:end]), + NoBoundaries() + ) + CAP_lin = LinearInterpolator( + Array(deploy_cap_cost[1, :]), Array(deploy_cap_cost[2, :]), NoBoundaries() + ) + + return (OP_lin.(scen_no_corals) .* scen_no_years .+ CAP_lin.(scen_no_corals)) ./ (10^6) +end + +end From 235b48817e87007b4bec7b4b692c563b10ddb8bf Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 28 May 2024 15:29:42 +1000 Subject: [PATCH 05/23] Add docs for economics functions --- src/analysis/economics.jl | 108 +++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index 0697c5c15..c8e9ca9aa 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -6,6 +6,90 @@ using BasicInterpolators using ADRIA: ResultSet using DataFrames, YAXArrays +""" + data_envelopment_analysis(X::YAXArray, Y::YAXArray; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata) + ::Tuple{DEA.AbstractDEAModel,AbstractArray} + data_envelopment_analysis(X::YAXArray, metrics...; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata) + ::Tuple{DEA.AbstractDEAModel,AbstractArray} + +Performs output-oriented (default) Data Envelopment Analysis (DEA) given inputs X and output +metrics Y. DEA is used to measure the performance of entities (scenarios), where inputs are +converted to outputs via some process. Each scenario's "efficiency score" is calculated +relative to an "efficiency fromtier", a region representing scenarios for which outputs +cannot be further increased by changing inputs (scenario settings). Scenarios on the +frontier serve as "benchmarks" or "peers", associated with best practice restoration +scenarios. Scenarios with efficiencies not equal to 1 can be improved to be more efficient. + + +# Arguments +- `X` : Model inputs for each scenario (usually costs). +- `Y` : Model outputs for each scenario (metrics such as tac, rci etc.). +- `rts` : Returns to scale, can be constant returns to scale (assumes all scenarios are at + their optimal scale, ie. a multiple would not be more optimal, `rts=:CRS`), or variable + returns to scale (doesn't assume the optimal scale has been acheived, `rts=:VRS`) +- `orient` : Orientation of the analysis. Can be output oriented (`orient=:Output`), which + seeks to maximise outputs for a given level of input, or input oriented (`orient=:Input`), + which seeks to minimise an input for a given level of output. + +# Returns +DEA.AbstractDEAModel, which summarizes efficiencies and slack variables (or multipliers +depending on the DEA method) for each scenario. Slack variables or multipliers give +information about the effort required to move an inefficient scenario towards it's peer +efficient scenario. X, a matrix of the outputs which can be used to plot the efficiency +frontier. + + +# Examples +```julia +dom = ADRIA.load_domain("example_domain") +scens = ADRIA.sample(dom, 128) +rs = ADRIA.run_scenarios(dom, scens, "45") + +# Get cost of deploying corals in each scenario +CAD_cost = ADRIA.economics.CAD_cost(scens) + +# Get mean coral cover and shelter volume for each scenario +s_tac = dropdims( + mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps +) +s_sv::Vector{Float64} = + dropdims( + mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:sites); + dims=(:timesteps, :sites) + ) + +# Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum +# deployment cost. +DEA_scens = ADRIA.economics.data_envelopment_analysis(CAD_cost, s_tac, s_sv) + +``` + +# References +1. Huguenin, J-M., 2012. + Data Envelopment Analysis (DEA): A pedagogical guide for decision makers in the public + sector. https://api.semanticscholar.org/CorpusID:188267263 +2. Pascoe, S., Cannard, T., Dowling, N.A., et. al., 2023. + Use of Data Envelopment Analysis (DEA) to assess management alternatives in the presence + of multiple objectives. + Marine Policy, 148, 105444. + https://doi.org/10.1016/j.marpol.2022.105444 +3. Pascoe, S., 2024. + On the use of Data Envelopement Analysis for Multi-Criteria Decision Analysis. + Algorithms, 17:89. + https://doi.org/10.3390/a17030089 + +""" +function data_envelopment_analysis( + X::YAXArray, metrics...; rts::Symbol=:VRS, + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +)::Tuple{DEA.AbstractDEAModel,AbstractArray} + Y = Array(hcat(metrics...)) + return data_envelopment_analysis( + X, Y; rts=rts, orient=orient, dea_model=dea_model + ) +end function data_envelopment_analysis( rs::ResultSet, cost::Vector{Float64}, metrics...; rts::Symbol=:VRS, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata @@ -23,7 +107,29 @@ function data_envelopment_analysis( return result, X end -function CAD_cost(scenarios::DataFrame; Reef::String="Moore") +""" + CAD_cost(rs; Reef::String="Moore")::YAXArray + CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray + +Calculate the cost of coral deployments for a set of scenarios. Based on piecewise linear + interpolations of cost data collected from `3.5.1 CA Deployment Model.xls`. Assumes the + ship Cape Ferguson is used and a 28 day deployment window (effects set-up cost only). + +# Arguments +- `rs` : ResultSet +- `scenarios` : sampled input scenario specifications. +- `Reef` : Reef to travel to (impacts cost significantly for <10000 devices deployed). + Currently cost data only available for ["Moore", "Davies", "Swains", "Keppel"], but + could be used as an approximation for nearby clusters. + +# Returns +YAXArray, containing estimated cost for each scenario in `scenarios`. + +""" +function CAD_cost(rs; Reef::String="Moore")::YAXArray + return CAD_cost(rs.inputs; Reef=Reef) +end +function CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray # No. of corals deployed in each scenario scen_no_corals = scenarios[:, :N_seed_CA] .+ scenarios[:, :N_seed_SM] .+ scenarios[:, :N_seed_TA] From 8bb731bf55d172e59ef3af8cc63934fd6f2a7e0d Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 28 May 2024 15:46:31 +1000 Subject: [PATCH 06/23] Add option to input ResultSet instead of cost vector (with optional cost function) --- src/analysis/economics.jl | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index c8e9ca9aa..f01d0a9f3 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -81,6 +81,24 @@ DEA_scens = ADRIA.economics.data_envelopment_analysis(CAD_cost, s_tac, s_sv) https://doi.org/10.3390/a17030089 """ +function data_envelopment_analysis(rs::ResultSet, metrics...; + input_function::Function=CAD_cost, rts::Symbol=:VRS, orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata +)::Tuple{DEA.AbstractDEAModel,AbstractArray} + X = input_function(rs.inputs) + return data_envelopment_analysis( + X, metrics; rts=rts, orient=orient, dea_model=dea_model + ) +end +function data_envelopment_analysis(rs::ResultSet, Y::YAXArray; + input_function::Function=CAD_cost, rts::Symbol=:VRS, orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata +)::Tuple{DEA.AbstractDEAModel,AbstractArray} + X = input_function(rs.inputs) + return data_envelopment_analysis( + X, Y; rts=rts, orient=orient, dea_model=dea_model + ) +end function data_envelopment_analysis( X::YAXArray, metrics...; rts::Symbol=:VRS, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata @@ -91,20 +109,19 @@ function data_envelopment_analysis( ) end function data_envelopment_analysis( - rs::ResultSet, cost::Vector{Float64}, metrics...; rts::Symbol=:VRS, + X::YAXArray, Y::YAXArray; rts::Symbol=:VRS, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata )::Tuple{DEA.AbstractDEAModel,AbstractArray} - X = Array(hcat(metrics...)) return data_envelopment_analysis( - rs, cost, X; rts=rts, orient=orient, dea_model=dea_model + Array(X), Array(Y); rts=rts, orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - rs::ResultSet, cost::Vector{Float64}, X::Matrix{Float64}; rts::Symbol=:VRS, + X::Vector{Float64}, Y::Matrix{Float64}; rts::Symbol=:VRS, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata )::Tuple{DEA.AbstractDEAModel,AbstractArray} - result = dea_model(Array(cost), X; orient=orient, rts=rts) - return result, X + result = dea_model(X, Y; orient=orient, rts=rts) + return result, Y end """ @@ -147,7 +164,10 @@ function CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray Array(deploy_cap_cost[1, :]), Array(deploy_cap_cost[2, :]), NoBoundaries() ) - return (OP_lin.(scen_no_corals) .* scen_no_years .+ CAP_lin.(scen_no_corals)) ./ (10^6) + return YAXArray( + (Dim{:scenarios}range(1:nrows(scenarios)),), + (OP_lin.(scen_no_corals) .* scen_no_years .+ CAP_lin.(scen_no_corals)) ./ (10^6) + ) end end From c149db79bc14b19849d43f89840f5acaf377991b Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 4 Jun 2024 14:33:04 +1000 Subject: [PATCH 07/23] Add `DEAResult` type and constructor, use this to package outputs Include 3 different returns to scale in outputs to allow returns to scale ratios to be calculated and plotted --- src/analysis/economics.jl | 99 +++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index f01d0a9f3..171c17441 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -1,4 +1,4 @@ -module economics +#module economics using CSV using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA @@ -6,13 +6,36 @@ using BasicInterpolators using ADRIA: ResultSet using DataFrames, YAXArrays +struct DEAResult{V,V2,V3} + crs_vals::V # IUnverse efficiencies using constant returns to scale. + vrs_vals::V # Inverse efficiencies using variable returns to scale. + fdh_vals::V # Inverse efficiencies using free disposability hull (non-convexity assumption). + crs_peers::V2 # Scenarios on the efficiency frontier. + vrs_peers::V2 # Scenarios on the efficiency frontier. + fdh_peers::V2 # Scenarios on the efficiency frontier. + X::V3 # Inputs + Y::V # Outputs +end + +function DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, + FDH_eff::Vector{Float64}, CRS_peers::Vector{Int64}, VRS_peers::Vector{Int64}, + FDH_peers::Vector{Int64}, X::Matrix{Float64}, Y::Vector{Float64} +)::DEAResult + return DEAResult(1 ./ CRS_eff, + 1 ./ VRS_eff, + 1 ./ FDH_eff, + CRS_peers, + VRS_peers, + FDH_peers, + X, + Y) +end + """ - data_envelopment_analysis(X::YAXArray, Y::YAXArray; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata) - ::Tuple{DEA.AbstractDEAModel,AbstractArray} - data_envelopment_analysis(X::YAXArray, metrics...; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata) - ::Tuple{DEA.AbstractDEAModel,AbstractArray} + data_envelopment_analysis(X::YAXArray, Y::YAXArray; orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::YAXArray, metrics...; orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata)::DEAResult Performs output-oriented (default) Data Envelopment Analysis (DEA) given inputs X and output metrics Y. DEA is used to measure the performance of entities (scenarios), where inputs are @@ -26,20 +49,12 @@ scenarios. Scenarios with efficiencies not equal to 1 can be improved to be more # Arguments - `X` : Model inputs for each scenario (usually costs). - `Y` : Model outputs for each scenario (metrics such as tac, rci etc.). -- `rts` : Returns to scale, can be constant returns to scale (assumes all scenarios are at - their optimal scale, ie. a multiple would not be more optimal, `rts=:CRS`), or variable - returns to scale (doesn't assume the optimal scale has been acheived, `rts=:VRS`) - `orient` : Orientation of the analysis. Can be output oriented (`orient=:Output`), which seeks to maximise outputs for a given level of input, or input oriented (`orient=:Input`), which seeks to minimise an input for a given level of output. # Returns -DEA.AbstractDEAModel, which summarizes efficiencies and slack variables (or multipliers -depending on the DEA method) for each scenario. Slack variables or multipliers give -information about the effort required to move an inefficient scenario towards it's peer -efficient scenario. X, a matrix of the outputs which can be used to plot the efficiency -frontier. - +DEAResult, which summarizes inputs, outputs, efficiencies and peers for each scenario. # Examples ```julia @@ -82,46 +97,60 @@ DEA_scens = ADRIA.economics.data_envelopment_analysis(CAD_cost, s_tac, s_sv) """ function data_envelopment_analysis(rs::ResultSet, metrics...; - input_function::Function=CAD_cost, rts::Symbol=:VRS, orient::Symbol=:Output, + input_function::Function=CAD_cost, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata -)::Tuple{DEA.AbstractDEAModel,AbstractArray} +)::DEAResult X = input_function(rs.inputs) return data_envelopment_analysis( - X, metrics; rts=rts, orient=orient, dea_model=dea_model + X, metrics; orient=orient, dea_model=dea_model ) end function data_envelopment_analysis(rs::ResultSet, Y::YAXArray; - input_function::Function=CAD_cost, rts::Symbol=:VRS, orient::Symbol=:Output, + input_function::Function=CAD_cost, orient::Symbol=:Output, dea_model::Function=DEA.deabigdata -)::Tuple{DEA.AbstractDEAModel,AbstractArray} +)::DEAResult X = input_function(rs.inputs) return data_envelopment_analysis( - X, Y; rts=rts, orient=orient, dea_model=dea_model + X, Y; orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::YAXArray, metrics...; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata -)::Tuple{DEA.AbstractDEAModel,AbstractArray} + X::YAXArray, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +)::DEAResult Y = Array(hcat(metrics...)) return data_envelopment_analysis( - X, Y; rts=rts, orient=orient, dea_model=dea_model + Array(X), Y; orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::YAXArray, Y::YAXArray; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata -)::Tuple{DEA.AbstractDEAModel,AbstractArray} + X::YAXArray, Y::YAXArray; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +)::DEAResult return data_envelopment_analysis( - Array(X), Array(Y); rts=rts, orient=orient, dea_model=dea_model + Array(X), Array(Y); orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::Vector{Float64}, Y::Matrix{Float64}; rts::Symbol=:VRS, - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata -)::Tuple{DEA.AbstractDEAModel,AbstractArray} - result = dea_model(X, Y; orient=orient, rts=rts) - return result, Y + X::Vector{Float64}, Y::Matrix{Float64}; orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata +)::DEAResult + result_CRS = dea_model(X, Y; orient=orient, rts=:CRS) + result_VRS = dea_model(X, Y; orient=orient, rts=:VRS) + result_FDH = dea_model(X, Y; orient=orient, rts=:FDH) + + CRS_peers = peers(result_CRS) + VRS_peers = peers(result_VRS) + FDH_peers = peers(result_FDH) + + return DEAResult( + result_CRS.eff, + result_VRS.eff, + result_FDH.eff, + CRS_peers, + VRS_peers, + FDH_peers, + X, + Y + ) end """ From 97917e2ffa6d5f7144eb0ad5753b2ac0944946e2 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 4 Jun 2024 14:35:19 +1000 Subject: [PATCH 08/23] Add documentation to CAD cost function and fix relationship between cost/coral and no of deployment years Add setting scenarios with zero years of deployment to zero to avoid div by zero error --- src/analysis/economics.jl | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index 171c17441..3d73ef919 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -176,15 +176,24 @@ function CAD_cost(rs; Reef::String="Moore")::YAXArray return CAD_cost(rs.inputs; Reef=Reef) end function CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray + # No. of deployment years + scen_no_years = scenarios[:, :seed_years] + # No. of corals deployed in each scenario scen_no_corals = - scenarios[:, :N_seed_CA] .+ scenarios[:, :N_seed_SM] .+ scenarios[:, :N_seed_TA] + ( + scenarios[:, :N_seed_CA] .+ scenarios[:, :N_seed_SM] .+ scenarios[:, :N_seed_TA] + ) ./ scen_no_years + scen_no_corals[scen_no_years .== 0.0] .= 0.0 - scen_no_years = scenarios[:, :seed_years] + # Operational and capital cost data to train models deploy_op_cost = CSV.read("deploy_op_cost.csv", DataFrame; header=false) deploy_cap_cost = CSV.read("deploy_cap_cost.csv", DataFrame; header=false) + + # Reef for deployment reef_ind = findfirst(deploy_op_cost[2:end, 1] .== Reef) + # Create interpolators based on cost data OP_lin = LinearInterpolator( Array(deploy_op_cost[1, 2:end]), Array(deploy_op_cost[reef_ind, 2:end]), NoBoundaries() @@ -193,10 +202,14 @@ function CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray Array(deploy_cap_cost[1, :]), Array(deploy_cap_cost[2, :]), NoBoundaries() ) + # Return costs (capital based on total no. of corals, operational based on corals/year) return YAXArray( - (Dim{:scenarios}range(1:nrows(scenarios)),), - (OP_lin.(scen_no_corals) .* scen_no_years .+ CAP_lin.(scen_no_corals)) ./ (10^6) + (Dim{:scenarios}(1:size(scenarios, 1)),), + (( + OP_lin.(scen_no_corals) .+ + CAP_lin.(scen_no_corals) + ) .* scen_no_years) ./ (10^6) ) end -end +#end From 2d366b10fca06ed4ab95315a758e3b06937c7664 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 5 Jun 2024 14:32:28 +1000 Subject: [PATCH 09/23] Add use of `DEAResult` struct to plot DEA analysis Now plots three plots, the efficiency frontier and data cloud, the technical efficiency and the scale efficiency. Also add docs for viz functions. --- ext/AvizExt/viz/economics.jl | 108 ++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 14 deletions(-) diff --git a/ext/AvizExt/viz/economics.jl b/ext/AvizExt/viz/economics.jl index 3832590f1..1f378b1df 100644 --- a/ext/AvizExt/viz/economics.jl +++ b/ext/AvizExt/viz/economics.jl @@ -1,38 +1,118 @@ +using ADRIA: DEAResult +""" + ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult;axis_opts=Dict(), + fig_opts=Dict(), opts=Dict()) + ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition},rs::ResultSet, + DEA_output::DEAResult; axis_opts=Dict(),opts=Dict()) + ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, DEA_output::DEAResult; + axis_opts=Dict(), opts=Dict()) + +Plot results from a DEA analysis. Plots the first 2 dimensions of the effificency frontier, +along side the technical and scale efficiencies. + +# Examples +``` +# Run scenarios +scens = ADRIA.sample(dom, 2^12) +rs = ADRIA.run_scenarios(dom, scens, ["45"]) + +# Compute cost an mean metrics for each scenario +CAD_cost = ADRIA.CAD_cost(scens) +s_tac::Vector{Float64} = Array( + dropdims( + mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps + ) +) +s_sv::Vector{Float64} = Array( + dropdims( + mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:sites); + dims=(:timesteps, :sites) + ) +) + +# Apply DEA analysis +DEA_output = ADRIA.data_envelopment_analysis(CAD_cost, s_tac, s_sv) + +# Plot frontier, sclae and technical efficiencies +ADRIA.viz.data_envelopment_analysis(rs, DEA_output) +``` + +# Arguments +- `rs` : ResultSet +- `DEA_output` : output structure from a DEA analysis, carried out using `data_envelopment_analysis` +- `opts` : Aviz options + - `frontier_type`, type of frontier to plot: "CRS","VRS" or "FDH". + - `line_color`, color to use for best practice frontier. + - `data_color`, color to use for data cloud when plotting efficiency frontier. + - `frontier_name`, text to label efficiency frontier. + - `data_name`, text to label data cloud used to plot efficiency frontier. +- `axis_opts` : Additional options to pass to adjust Axis attributes + See: https://docs.makie.org/v0.19/api/index.html#Axis +- `fig_opts` : Additional options to pass to adjust Figure creation + See: https://docs.makie.org/v0.19/api/index.html#Figure +""" function ADRIA.viz.data_envelopment_analysis( - rs::ResultSet, costs::Vector{Float64}, X::Vector{Float64}, efficiencies::Array{Float64}; - axis_opts=Dict(), + rs::ResultSet, DEA_output::DEAResult; + axis_opts=Dict(), fig_opts=Dict(), opts=Dict() ) - f = Figure() - g = Axis(f[1, 1]; axis_opts...) - return ADRIA.viz.data_envelopment_analysis(g, rs, costs, X, efficiencies; opts=opts) + f = Figure(; fig_opts...) + g = f[1, 1] = GridLayout() + + return ADRIA.viz.data_envelopment_analysis( + g, rs, DEA_output; opts=opts, axis_opts=axis_opts + ) end function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - rs::ResultSet, costs::Vector{Float64}, X::Vector{Float64}, efficiencies::Array{Float64}; + rs::ResultSet, DEA_output::DEAResult; axis_opts=Dict(), opts=Dict() ) return ADRIA.viz.data_envelopment_analysis( - g, costs, X, efficiencies; opts=opts + g, DEA_output; opts=opts, axis_opts=axis_opts ) end function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - Y::Vector{Float64}, X::Vector{Float64}, - efficiencies::Vector{Float64}; opts=Dict()) + DEA_output::DEAResult; axis_opts=Dict(), opts=Dict()) line_color = get(opts, :line_color, :red) data_color = get(opts, :data_color, :black) frontier_name = get(opts, :frontier_name, "Best practice frontier") data_name = get(opts, :data_name, "Scenario data cloud") + # Determines which returns to scale approach is used to select scenario peers + # (most efficient scenarios) + frontier_type = get(opts, :frontier_type, "VRS") + + ga = g[1, 1] = GridLayout() + gb = g[2, 1] = GridLayout() + gc = g[3, 1] = GridLayout() + + X = DEA_output.X # Find points on best practice frontier - best_practice_scens = findall(efficiencies .== 1.0) + if frontier_type == "VRS" + best_practice_scens = DEA_output.VRS_peers.J + elseif frontier_type == "CRS" + best_practice_scens = DEA_output.CRS_peers.J + else + best_practice_scens = DEA_output.FDH_peers.J + end - # Plot frontier and data cloud + scale_efficiency = DEA_output.CRS_eff ./ DEA_output.VRS_eff + + # Plot efficiency frontier and data cloud + axa = Axis(ga; axis_opts...) frontier = lines!( - g, Y[best_practice_scens], X[best_practice_scens]; color=line_color + axa, X[best_practice_scens, 1], X[best_practice_scens, 2]; color=line_color ) - data = scatter!(g, Y, X; color=data_color) - Legend(g, [frontier, data], [frontier_name, data_name]) + data = scatter!(axa, X[:, 1], X[:, 2]; color=data_color) + Legend(ax, [frontier, data], [frontier_name, data_name]) + + # Plot the scale efficiency (ratio of efficiencies assuming CRS vs. assuming VRS) + axb = Axis(gb; axis_opts...) + scatter!(axb, scale_efficiency; color=data_color, title="Scale efficiency") + # Plot the technical efficiency (inverse VRS efficiencies) + axc = Axis(gc; axis_opts...) + scatter!(axc, DEA_output.VRS_eff; color=data_color, title="Technical efficiency") return g end From 8a77357e4db1e2486a4fc400bfa9fb9bfdeb4487 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 5 Jun 2024 14:33:19 +1000 Subject: [PATCH 10/23] Add economics.jl import and `data_envelopment_analysis` to viz ext --- ext/AvizExt/viz/viz.jl | 1 + src/viz/viz.jl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ext/AvizExt/viz/viz.jl b/ext/AvizExt/viz/viz.jl index 9e19f21d8..bb6f28556 100644 --- a/ext/AvizExt/viz/viz.jl +++ b/ext/AvizExt/viz/viz.jl @@ -102,3 +102,4 @@ include("spatial.jl") include("taxa_dynamics.jl") include("environment/dhw.jl") include("environment/cyclones.jl") +include("economics.jl") diff --git a/src/viz/viz.jl b/src/viz/viz.jl index 96a2b657e..6a75a5602 100644 --- a/src/viz/viz.jl +++ b/src/viz/viz.jl @@ -53,4 +53,7 @@ function cyclone_scenario() end function taxonomy() end function taxonomy!() end +# Economics +function data_envelopment_analysis() end + end # module From c3cbe22d1192067eae8a2f693f00203c0397f493 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 5 Jun 2024 15:51:24 +1000 Subject: [PATCH 11/23] Add docs for DEAResult constructor --- src/analysis/economics.jl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index 3d73ef919..d3bb526f3 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -7,7 +7,7 @@ using ADRIA: ResultSet using DataFrames, YAXArrays struct DEAResult{V,V2,V3} - crs_vals::V # IUnverse efficiencies using constant returns to scale. + crs_vals::V # Inverse efficiencies using constant returns to scale. vrs_vals::V # Inverse efficiencies using variable returns to scale. fdh_vals::V # Inverse efficiencies using free disposability hull (non-convexity assumption). crs_peers::V2 # Scenarios on the efficiency frontier. @@ -17,6 +17,23 @@ struct DEAResult{V,V2,V3} Y::V # Outputs end +""" + DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, FDH_eff::Vector{Float64}, + CRS_peers::Vector{Int64}, VRS_peers::Vector{Int64}, FDH_peers::Vector{Int64}, + X::Matrix{Float64}, Y::Vector{Float64})::DEAResult + +Constructor for DEAResult type. + +# Arguments +- `CRS_eff` : efficiencies from CRS DEA analysis. +- `VRS_eff` : efficiencies from VRS DEA analysis. +- `FDH_eff` : efficiencies from FDH DEA analysis. +- `CRS_peers` : peers indices from CRS DEA analysis. +- `VRS_peers` : peers indices from VRS DEA analysis. +- `FDH_peers` : peers indices from FDH DEA analysis. +- `X` : inputs. +- `Y` : outputs. +""" function DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, FDH_eff::Vector{Float64}, CRS_peers::Vector{Int64}, VRS_peers::Vector{Int64}, FDH_peers::Vector{Int64}, X::Matrix{Float64}, Y::Vector{Float64} From c8b4b9f94beda65cea61810e649a260865084712 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Fri, 7 Jun 2024 15:59:44 +1000 Subject: [PATCH 12/23] Add `cf_difference_scenario()` for calculating the bootstrapped difference to counterfactuals for each intervention scenario --- src/metrics/scenario.jl | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/metrics/scenario.jl b/src/metrics/scenario.jl index 868fd4906..bba823e16 100644 --- a/src/metrics/scenario.jl +++ b/src/metrics/scenario.jl @@ -1,3 +1,6 @@ +using Bootstrap +using Random + """Scenario-level summaries. Note: Aggregates across the `site` dimension so trajectories over time for each scenario are @@ -279,3 +282,72 @@ function scenario_outcomes(rs::ResultSet, metrics::Vector{<:Metric})::YAXArray return scen_outcomes end + +""" + cf_difference_scenario(rs::ResultSet, metric::Function) + cf_difference_scenario(scens::DataFrame, outcomes::Union{YAXArray{Float64,3}, + YAXArray{Float64,2},YAXArray{Float32,3},YAXArray{Float32,2}}) + cf_difference_scenario( + scens::DataFrame, outcomes::Union{YAXArray{Float64,1},YAXArray{Float32,1}}) + +Get bootstrapped difference between each intervention and conterfactual scenarios. + +# Arguments +- `rs` : ResultSet +- `metric` : Function to use to calculate metric. +- `scens` : Scenario DataFrame +`outcomes` : YAXArray for a metric (can have 1-3 dimensions which must include :scenarios) + +# Returns +YAXArray with (:timesteps, :scenarios, :outcomes) + +# Examples +``` +metrics::Vector{ADRIA.metrics.Metric} = [ + ADRIA.metrics.scenario_total_cover, + ADRIA.metrics.scenario_asv, + ADRIA.metrics.scenario_absolute_juveniles, +] + +# 3-dimensional Array of outcomes +outcomes = ADRIA.metrics.scenario_outcomes(rs, metrics) +``` +""" +function cf_difference_scenario(rs::ResultSet, metric::Function) + scens = rs.outputs # Get scenario dataframe + outcomes = metric(rs) # Extract some metric + return cf_difference_scenario(scens, outcomes) +end +function cf_difference_scenario( + scens::DataFrame, + outcomes::Union{ + YAXArray{Float64,3},YAXArray{Float64,2},YAXArray{Float32,3},YAXArray{Float32,2} + } +) + # Get metric mean over all dims except scenarios + outcome_dims = axes_names(outcomes)[findall(axes_names(outcomes) .!= :scenarios)] + outcomes_mean = dropdims(mean(outcomes; dims=outcome_dims); dims=outcome_dims) + return cf_difference_scenario(scens, outcomes_mean) +end +function cf_difference_scenario( + scens::DataFrame, outcomes::Union{YAXArray{Float64,1},YAXArray{Float32,1}} +) + itv_scenarios = findall(scens.guided .>= 0) # All intervention scenario IDs + cf_scenarios = findall(scens.guided .== -1) # All counterfactual scenario IDs + result = ZeroDataCube(; T=Float64, itv_scenarios=itv_scenarios) # Storage + cf_outcomes = outcomes[scenarios=cf_scenarios] + itv_outcomes = outcomes[scenarios=itv_scenarios] + + n_scens_itv = length(itv_scenarios) + n_scens_cf = length(cf_scenarios) + + for scen in 1:n_scens_itv + cf_shuf_set = shuffle(1:n_scens_cf) # shuffle counterfactual scenarios + itv_diff = collect(itv_outcomes[scen] .- cf_outcomes[cf_shuf_set]) + + # bootstap difference between an intervention scenario and counterfactuals + result[scen] = bootstrap(median, itv_diff, BalancedSampling(100)).t0[1] + end + + return result +end From 73fd1d9da69288f2d84e0b9d5a54ec619eb5b5ba Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Mon, 28 Oct 2024 09:31:17 +1000 Subject: [PATCH 13/23] Changes to make DEA agnostic to input/output functions Fix typos, remove cost function from input to `data_envelopment_analysis` --- ext/AvizExt/viz/economics.jl | 83 +++++++++++++++------------ src/analysis/deploy_cap_cost.csv | 2 - src/analysis/deploy_op_cost.csv | 5 -- src/analysis/economics.jl | 97 ++++++-------------------------- src/viz/viz.jl | 1 + 5 files changed, 65 insertions(+), 123 deletions(-) delete mode 100644 src/analysis/deploy_cap_cost.csv delete mode 100644 src/analysis/deploy_op_cost.csv diff --git a/ext/AvizExt/viz/economics.jl b/ext/AvizExt/viz/economics.jl index 1f378b1df..d977abbee 100644 --- a/ext/AvizExt/viz/economics.jl +++ b/ext/AvizExt/viz/economics.jl @@ -1,4 +1,4 @@ -using ADRIA: DEAResult +using ADRIA.economics: DEAResult """ ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult;axis_opts=Dict(), @@ -18,7 +18,7 @@ scens = ADRIA.sample(dom, 2^12) rs = ADRIA.run_scenarios(dom, scens, ["45"]) # Compute cost an mean metrics for each scenario -CAD_cost = ADRIA.CAD_cost(scens) +cost = cost_function(scens) s_tac::Vector{Float64} = Array( dropdims( mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps @@ -32,9 +32,9 @@ s_sv::Vector{Float64} = Array( ) # Apply DEA analysis -DEA_output = ADRIA.data_envelopment_analysis(CAD_cost, s_tac, s_sv) +DEA_output = ADRIA.data_envelopment_analysis(cost, s_tac, s_sv) -# Plot frontier, sclae and technical efficiencies +# Plot frontier, scale and technical efficiencies ADRIA.viz.data_envelopment_analysis(rs, DEA_output) ``` @@ -54,65 +54,76 @@ ADRIA.viz.data_envelopment_analysis(rs, DEA_output) """ function ADRIA.viz.data_envelopment_analysis( rs::ResultSet, DEA_output::DEAResult; - axis_opts=Dict(), fig_opts=Dict(), - opts=Dict() + axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), fig_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), + opts::OPT_TYPE=DEFAULT_OPT_TYPE() ) f = Figure(; fig_opts...) g = f[1, 1] = GridLayout() - return ADRIA.viz.data_envelopment_analysis( + ADRIA.viz.data_envelopment_analysis!( g, rs, DEA_output; opts=opts, axis_opts=axis_opts ) + + return f end -function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - rs::ResultSet, DEA_output::DEAResult; axis_opts=Dict(), - opts=Dict() +function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, + rs::ResultSet, DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), + opts::OPT_TYPE=DEFAULT_OPT_TYPE() ) - return ADRIA.viz.data_envelopment_analysis( + return ADRIA.viz.data_envelopment_analysis!( g, DEA_output; opts=opts, axis_opts=axis_opts ) end -function ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, - DEA_output::DEAResult; axis_opts=Dict(), opts=Dict()) - line_color = get(opts, :line_color, :red) +function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, + DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), + opts::OPT_TYPE=DEFAULT_OPT_TYPE()) + frontier_color = get(opts, :frontier_color, :red) data_color = get(opts, :data_color, :black) frontier_name = get(opts, :frontier_name, "Best practice frontier") data_name = get(opts, :data_name, "Scenario data cloud") + scale_eff_y_lab = get(opts, :scale_eff_y_lab, L"$\frac{eff_{vrs}}{eff_{crs}}$") + tech_eff_y_lab = get(opts, :tech_eff_y_lab, L"$\frac{1}{eff_{vrs}}$") + metrics_x_lab = get(opts, :metrics_x_lab, L"$metric 1$") + metrics_y_lab = get(opts, :metrics_y_lab, L"$metric 2$") # Determines which returns to scale approach is used to select scenario peers # (most efficient scenarios) - frontier_type = get(opts, :frontier_type, "VRS") + frontier_type = get(opts, :frontier_type, :vrs_peers) - ga = g[1, 1] = GridLayout() - gb = g[2, 1] = GridLayout() - gc = g[3, 1] = GridLayout() + Y = DEA_output.Y # Output values - X = DEA_output.X # Find points on best practice frontier - if frontier_type == "VRS" - best_practice_scens = DEA_output.VRS_peers.J - elseif frontier_type == "CRS" - best_practice_scens = DEA_output.CRS_peers.J - else - best_practice_scens = DEA_output.FDH_peers.J - end + best_practice_scens = getfield(DEA_output, frontier_type).J - scale_efficiency = DEA_output.CRS_eff ./ DEA_output.VRS_eff + scale_efficiency = DEA_output.crs_vals ./ DEA_output.vrs_vals # Plot efficiency frontier and data cloud - axa = Axis(ga; axis_opts...) - frontier = lines!( - axa, X[best_practice_scens, 1], X[best_practice_scens, 2]; color=line_color + axa = Axis(g[1, 1]; xlabel=metrics_x_lab, ylabel=metrics_y_lab, axis_opts...) + data = scatter!(axa, Y[:, 1], Y[:, 2]; color=data_color) + frontier = scatter!( + axa, Y[best_practice_scens, 1], Y[best_practice_scens, 2]; color=frontier_color ) - data = scatter!(axa, X[:, 1], X[:, 2]; color=data_color) - Legend(ax, [frontier, data], [frontier_name, data_name]) + Legend(g[1, 2], [frontier, data], [frontier_name, data_name]) # Plot the scale efficiency (ratio of efficiencies assuming CRS vs. assuming VRS) - axb = Axis(gb; axis_opts...) - scatter!(axb, scale_efficiency; color=data_color, title="Scale efficiency") + axb = Axis(g[2, 1]; title="Scale efficiency", ylabel=scale_eff_y_lab, axis_opts...) + scatter!(axb, scale_efficiency; color=data_color) + scatter!( + axb, + best_practice_scens, + scale_efficiency[best_practice_scens]; + color=frontier_color + ) # Plot the technical efficiency (inverse VRS efficiencies) - axc = Axis(gc; axis_opts...) - scatter!(axc, DEA_output.VRS_eff; color=data_color, title="Technical efficiency") + axc = Axis(g[3, 1]; title="Technical efficiency", ylabel=tech_eff_y_lab, axis_opts...) + scatter!(axc, DEA_output.vrs_vals; color=data_color) + scatter!( + axc, + best_practice_scens, + DEA_output.vrs_vals[best_practice_scens]; + color=frontier_color + ) + return g end diff --git a/src/analysis/deploy_cap_cost.csv b/src/analysis/deploy_cap_cost.csv deleted file mode 100644 index 68844656d..000000000 --- a/src/analysis/deploy_cap_cost.csv +++ /dev/null @@ -1,2 +0,0 @@ -100,500,1000,5000,10000,50000,90000,200000,250000,390000,450000,500000,600000,800000,1000000,2000000,4000000,6000000,8000000,10000000 -177470,177470,177470,177470,177470,177470,185470,193470,201470,378940,386940,394940,402940,418940,612410,1047350,2094700,3142050,4189400,5067280 diff --git a/src/analysis/deploy_op_cost.csv b/src/analysis/deploy_op_cost.csv deleted file mode 100644 index 4bdca6853..000000000 --- a/src/analysis/deploy_op_cost.csv +++ /dev/null @@ -1,5 +0,0 @@ -Reef/No. of Devices,100,500,1000,5000,10000,50000,100000,500000,1000000,5000000,10000000 -Moore,387.97,77.59,38.8,13.82,7.26,3.08,2.69,2.59,2.55,2.54,2.53 -Davies,631.97,126.39,63.2,12.99,9.7,3.57,3.22,2.9,2.87,2.83,2.82 -Swains,1319.97,263.99,132,26.75,16.58,4.95,4.88,3.89,3.87,2.32,3.85 -Keppel,462.97,92.59,46.3,9.61,5.15,3.23,2.77,2.46,2.42,2.4,2.39 diff --git a/src/analysis/economics.jl b/src/analysis/economics.jl index d3bb526f3..89914d1aa 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/economics.jl @@ -1,8 +1,6 @@ -#module economics +module economics -using CSV using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA -using BasicInterpolators using ADRIA: ResultSet using DataFrames, YAXArrays @@ -13,13 +11,13 @@ struct DEAResult{V,V2,V3} crs_peers::V2 # Scenarios on the efficiency frontier. vrs_peers::V2 # Scenarios on the efficiency frontier. fdh_peers::V2 # Scenarios on the efficiency frontier. - X::V3 # Inputs - Y::V # Outputs + X::V # Inputs + Y::V3 # Outputs end """ DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, FDH_eff::Vector{Float64}, - CRS_peers::Vector{Int64}, VRS_peers::Vector{Int64}, FDH_peers::Vector{Int64}, + CRS_peers::DEA.DEAPeers, VRS_peers::DEA.DEAPeers, FDH_peers::DEA.DEAPeers, X::Matrix{Float64}, Y::Vector{Float64})::DEAResult Constructor for DEAResult type. @@ -35,8 +33,8 @@ Constructor for DEAResult type. - `Y` : outputs. """ function DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, - FDH_eff::Vector{Float64}, CRS_peers::Vector{Int64}, VRS_peers::Vector{Int64}, - FDH_peers::Vector{Int64}, X::Matrix{Float64}, Y::Vector{Float64} + FDH_eff::Vector{Float64}, CRS_peers::DEA.DEAPeers, VRS_peers::DEA.DEAPeers, + FDH_peers::DEA.DEAPeers, X::Matrix{Float64}, Y::Vector{Float64} )::DEAResult return DEAResult(1 ./ CRS_eff, 1 ./ VRS_eff, @@ -79,8 +77,8 @@ dom = ADRIA.load_domain("example_domain") scens = ADRIA.sample(dom, 128) rs = ADRIA.run_scenarios(dom, scens, "45") -# Get cost of deploying corals in each scenario -CAD_cost = ADRIA.economics.CAD_cost(scens) +# Get cost of deploying corals in each scenario, with user-specified function +cost = cost_function(scens) # Get mean coral cover and shelter volume for each scenario s_tac = dropdims( @@ -94,7 +92,7 @@ s_sv::Vector{Float64} = # Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum # deployment cost. -DEA_scens = ADRIA.economics.data_envelopment_analysis(CAD_cost, s_tac, s_sv) +DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, s_tac, s_sv) ``` @@ -108,23 +106,21 @@ DEA_scens = ADRIA.economics.data_envelopment_analysis(CAD_cost, s_tac, s_sv) Marine Policy, 148, 105444. https://doi.org/10.1016/j.marpol.2022.105444 3. Pascoe, S., 2024. - On the use of Data Envelopement Analysis for Multi-Criteria Decision Analysis. + On the use of Data Envelopment Analysis for Multi-Criteria Decision Analysis. Algorithms, 17:89. https://doi.org/10.3390/a17030089 """ -function data_envelopment_analysis(rs::ResultSet, metrics...; - input_function::Function=CAD_cost, orient::Symbol=:Output, - dea_model::Function=DEA.deabigdata +function data_envelopment_analysis(rs::ResultSet, input_function::Function, + metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata )::DEAResult X = input_function(rs.inputs) return data_envelopment_analysis( X, metrics; orient=orient, dea_model=dea_model ) end -function data_envelopment_analysis(rs::ResultSet, Y::YAXArray; - input_function::Function=CAD_cost, orient::Symbol=:Output, - dea_model::Function=DEA.deabigdata +function data_envelopment_analysis(rs::ResultSet, Y::YAXArray, input_function::Function; + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata )::DEAResult X = input_function(rs.inputs) return data_envelopment_analysis( @@ -154,9 +150,9 @@ function data_envelopment_analysis( result_VRS = dea_model(X, Y; orient=orient, rts=:VRS) result_FDH = dea_model(X, Y; orient=orient, rts=:FDH) - CRS_peers = peers(result_CRS) - VRS_peers = peers(result_VRS) - FDH_peers = peers(result_FDH) + CRS_peers = DEA.peers(result_CRS) + VRS_peers = DEA.peers(result_VRS) + FDH_peers = DEA.peers(result_FDH) return DEAResult( result_CRS.eff, @@ -170,63 +166,4 @@ function data_envelopment_analysis( ) end -""" - CAD_cost(rs; Reef::String="Moore")::YAXArray - CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray - -Calculate the cost of coral deployments for a set of scenarios. Based on piecewise linear - interpolations of cost data collected from `3.5.1 CA Deployment Model.xls`. Assumes the - ship Cape Ferguson is used and a 28 day deployment window (effects set-up cost only). - -# Arguments -- `rs` : ResultSet -- `scenarios` : sampled input scenario specifications. -- `Reef` : Reef to travel to (impacts cost significantly for <10000 devices deployed). - Currently cost data only available for ["Moore", "Davies", "Swains", "Keppel"], but - could be used as an approximation for nearby clusters. - -# Returns -YAXArray, containing estimated cost for each scenario in `scenarios`. - -""" -function CAD_cost(rs; Reef::String="Moore")::YAXArray - return CAD_cost(rs.inputs; Reef=Reef) -end -function CAD_cost(scenarios::DataFrame; Reef::String="Moore")::YAXArray - # No. of deployment years - scen_no_years = scenarios[:, :seed_years] - - # No. of corals deployed in each scenario - scen_no_corals = - ( - scenarios[:, :N_seed_CA] .+ scenarios[:, :N_seed_SM] .+ scenarios[:, :N_seed_TA] - ) ./ scen_no_years - scen_no_corals[scen_no_years .== 0.0] .= 0.0 - - # Operational and capital cost data to train models - deploy_op_cost = CSV.read("deploy_op_cost.csv", DataFrame; header=false) - deploy_cap_cost = CSV.read("deploy_cap_cost.csv", DataFrame; header=false) - - # Reef for deployment - reef_ind = findfirst(deploy_op_cost[2:end, 1] .== Reef) - - # Create interpolators based on cost data - OP_lin = LinearInterpolator( - Array(deploy_op_cost[1, 2:end]), Array(deploy_op_cost[reef_ind, 2:end]), - NoBoundaries() - ) - CAP_lin = LinearInterpolator( - Array(deploy_cap_cost[1, :]), Array(deploy_cap_cost[2, :]), NoBoundaries() - ) - - # Return costs (capital based on total no. of corals, operational based on corals/year) - return YAXArray( - (Dim{:scenarios}(1:size(scenarios, 1)),), - (( - OP_lin.(scen_no_corals) .+ - CAP_lin.(scen_no_corals) - ) .* scen_no_years) ./ (10^6) - ) end - -#end diff --git a/src/viz/viz.jl b/src/viz/viz.jl index 6a75a5602..79ce897e6 100644 --- a/src/viz/viz.jl +++ b/src/viz/viz.jl @@ -55,5 +55,6 @@ function taxonomy!() end # Economics function data_envelopment_analysis() end +function data_envelopment_analysis!() end end # module From f91e872dd8615cedf66a8c551571b1b2d34c6625 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Mon, 28 Oct 2024 13:24:07 +1000 Subject: [PATCH 14/23] Include DataEnvelopementAnalysis.jl in ADRIA Manifest --- Manifest-v1.11.toml | 164 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 34 deletions(-) diff --git a/Manifest-v1.11.toml b/Manifest-v1.11.toml index c46938fd2..36101ba76 100644 --- a/Manifest-v1.11.toml +++ b/Manifest-v1.11.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.1" +julia_version = "1.11.0" manifest_format = "2.0" -project_hash = "f5f80681e1f41a61ae2064c46066ad6e608da783" +project_hash = "6ea7e541a27709a6f9e32ca4b0816717a46eb307" [[deps.ARFFFiles]] deps = ["CategoricalArrays", "Dates", "Parsers", "Tables"] @@ -10,6 +10,12 @@ git-tree-sha1 = "678eb18590a8bc6674363da4d5faa4ac09c40a18" uuid = "da404889-ca92-49ff-9e8b-0aa6b4d38dc8" version = "1.5.0" +[[deps.ASL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6252039f98492252f9e47c312c8ffda0e3b9e78d" +uuid = "ae81ac8f-d209-56e5-92de-9978fef736f9" +version = "0.1.3+0" + [[deps.AWS]] deps = ["Base64", "Compat", "Dates", "Downloads", "GitHub", "HTTP", "IniFile", "JSON", "MbedTLS", "Mocking", "OrderedCollections", "Random", "SHA", "Sockets", "URIs", "UUIDs", "XMLDict"] git-tree-sha1 = "319ade7f8fc88243369e119859a7d3a3e7e7f267" @@ -71,9 +77,9 @@ version = "0.1.38" [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099" +git-tree-sha1 = "d80af0733c99ea80575f612813fa6aa71022d33a" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.0.4" +version = "4.1.0" weakdeps = ["StaticArrays"] [deps.Adapt.extensions] @@ -207,6 +213,12 @@ git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e" uuid = "9718e550-a3fa-408a-8086-8db961cd8217" version = "0.1.1" +[[deps.BenchmarkTools]] +deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.5.0" + [[deps.BitFlags]] git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" @@ -310,6 +322,12 @@ git-tree-sha1 = "9ebb045901e9bbf58767a9f34ff89831ed711aae" uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" version = "0.15.7" +[[deps.CodecBzip2]] +deps = ["Bzip2_jll", "TranscodingStreams"] +git-tree-sha1 = "e7c529cc31bb85b97631b922fa2e6baf246f5905" +uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd" +version = "0.8.4" + [[deps.CodecInflate64]] deps = ["TranscodingStreams"] git-tree-sha1 = "d981a6e8656b1e363a2731716f46851a2257deb7" @@ -445,6 +463,12 @@ git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.16.0" +[[deps.DataEnvelopmentAnalysis]] +deps = ["Distributed", "Distributions", "GLPK", "InvertedIndices", "Ipopt", "JuMP", "LinearAlgebra", "PrecompileTools", "Printf", "ProgressMeter", "Random", "SharedArrays", "SparseArrays", "Statistics", "StatsAPI", "StatsBase"] +git-tree-sha1 = "d79c7c880808ca66f24b65d8e064b8eca43c183a" +uuid = "a100299e-89d6-11e9-0fa0-2daf497e6a05" +version = "0.9.1" + [[deps.DataFrames]] deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] git-tree-sha1 = "fb61b4812c49343d7ef0b533ba982c46021938a6" @@ -512,15 +536,15 @@ version = "0.1.10" [[deps.DiskArrays]] deps = ["LRUCache", "OffsetArrays"] -git-tree-sha1 = "dea23e3d787c25fbf5f879c4501da828bc729472" +git-tree-sha1 = "e0e89a60637a62d13aa2107f0acd169b9b9b77e7" uuid = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" -version = "0.4.5" +version = "0.4.6" [[deps.Distances]] deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" +git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.11" +version = "0.10.12" weakdeps = ["ChainRulesCore", "SparseArrays"] [deps.Distances.extensions] @@ -742,6 +766,18 @@ git-tree-sha1 = "273bd1cd30768a2fddfa3fd63bbc746ed7249e5f" uuid = "38e38edf-8417-5370-95a0-9cbb8c7f171a" version = "1.9.0" +[[deps.GLPK]] +deps = ["GLPK_jll", "MathOptInterface"] +git-tree-sha1 = "1d706bd23e5d2d407bfd369499ee6f96afb0c3ad" +uuid = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" +version = "1.2.1" + +[[deps.GLPK_jll]] +deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "fe68622f32828aa92275895fdb324a85894a5b1b" +uuid = "e8aa6df9-e6ca-548a-97ff-1f85fc5b8b98" +version = "5.0.1+0" + [[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" @@ -1007,6 +1043,18 @@ git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" version = "1.3.0" +[[deps.Ipopt]] +deps = ["Ipopt_jll", "LinearAlgebra", "MathOptInterface", "OpenBLAS32_jll", "PrecompileTools"] +git-tree-sha1 = "76315a7100e9e901d959c25b6ab6d4a37e799132" +uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" +version = "1.6.7" + +[[deps.Ipopt_jll]] +deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] +git-tree-sha1 = "a0950d209a055b3adb6d29ade5cbdf005a6bd290" +uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" +version = "300.1400.1600+0" + [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" @@ -1064,6 +1112,16 @@ git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "3.0.4+0" +[[deps.JuMP]] +deps = ["LinearAlgebra", "MacroTools", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays"] +git-tree-sha1 = "c91f872c6150cf1471f9cb279f5e0dc09423bdcf" +uuid = "4076af6c-e467-56ae-b986-b466b2749572" +version = "1.23.3" +weakdeps = ["DimensionalData"] + + [deps.JuMP.extensions] + JuMPDimensionalDataExt = "DimensionalData" + [[deps.JuliaVariables]] deps = ["MLStyle", "NameResolution"] git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" @@ -1083,9 +1141,9 @@ version = "1.19.3+0" [[deps.KernelAbstractions]] deps = ["Adapt", "Atomix", "InteractiveUtils", "MacroTools", "PrecompileTools", "Requires", "StaticArrays", "UUIDs", "UnsafeAtomics", "UnsafeAtomicsLLVM"] -git-tree-sha1 = "04e52f596d0871fa3890170fa79cb15e481e4cd8" +git-tree-sha1 = "e73a077abc7fe798fe940deabe30ef6c66bdde52" uuid = "63c18a36-062a-441e-b654-da1e3ab1ce7c" -version = "0.9.28" +version = "0.9.29" [deps.KernelAbstractions.extensions] EnzymeExt = "EnzymeCore" @@ -1104,10 +1162,10 @@ uuid = "88015f11-f218-50d7-93a8-a6af411a945d" version = "4.0.0+0" [[deps.LLVM]] -deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Requires", "Unicode"] -git-tree-sha1 = "4ad43cb0a4bb5e5b1506e1d1f48646d7e0c80363" +deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Preferences", "Printf", "Unicode"] +git-tree-sha1 = "d422dfd9707bec6617335dc2ea3c5172a87d5908" uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "9.1.2" +version = "9.1.3" [deps.LLVM.extensions] BFloat16sExt = "BFloat16s" @@ -1206,9 +1264,9 @@ version = "1.11.0" [[deps.Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" +git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" +version = "1.11.0+0" [[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -1218,15 +1276,15 @@ version = "1.6.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" +git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" +version = "1.50.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" +version = "1.17.0+1" [[deps.Libtiff_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] @@ -1285,9 +1343,9 @@ version = "1.11.0" [[deps.LoggingExtras]] deps = ["Dates", "Logging"] -git-tree-sha1 = "c1dd6d7978c12545b4179fb6153b9250c96b0075" +git-tree-sha1 = "f02b56007b064fbfddb4c9cd60161b6dd0f40df3" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.0.3" +version = "1.1.0" [[deps.Lz4_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1301,6 +1359,12 @@ git-tree-sha1 = "1d2dd9b186742b0f317f2530ddcbf00eebb18e96" uuid = "23992714-dd62-5051-b70f-ba57cb901cac" version = "0.10.7" +[[deps.METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.2+0" + [[deps.MLFlowClient]] deps = ["Dates", "FilePathsBase", "HTTP", "JSON", "ShowCases", "URIs", "UUIDs"] git-tree-sha1 = "9abb12b62debc27261c008daa13627255bf79967" @@ -1400,6 +1464,12 @@ git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" version = "5.5.1+0" +[[deps.MUMPS_seq_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] +git-tree-sha1 = "85047ac569761e3387717480a38a61d2a67df45c" +uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" +version = "500.700.300+0" + [[deps.MacroTools]] deps = ["Markdown", "Random"] git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" @@ -1422,6 +1492,12 @@ deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" version = "1.11.0" +[[deps.MathOptInterface]] +deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] +git-tree-sha1 = "c7fff4fbc415a6833988efbda7621039af2a5f36" +uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +version = "1.32.0" + [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" @@ -1483,6 +1559,12 @@ version = "0.3.4" uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.12.12" +[[deps.MutableArithmetics]] +deps = ["LinearAlgebra", "SparseArrays", "Test"] +git-tree-sha1 = "90077f1e79de8c9c7c8a90644494411111f4e07b" +uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" +version = "1.5.2" + [[deps.NLSolversBase]] deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" @@ -1583,6 +1665,12 @@ git-tree-sha1 = "a5a5a68d079ce531b0220e99789e0c1c8c5ed215" uuid = "925886fa-5bf2-5e8e-b522-a9147a512338" version = "1.7.1" +[[deps.OpenBLAS32_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dd806c813429ff09878ea3eeb317818f3ca02871" +uuid = "656ef2d0-ae68-5445-9ca0-591084a874a2" +version = "0.3.28+3" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" @@ -1646,13 +1734,11 @@ deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", git-tree-sha1 = "d9b79c4eed437421ac4285148fcadf42e0700e89" uuid = "429524aa-4258-5aef-a3af-852621145aeb" version = "1.9.4" +weakdeps = ["MathOptInterface"] [deps.Optim.extensions] OptimMOIExt = "MathOptInterface" - [deps.Optim.weakdeps] - MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" - [[deps.OrderedCollections]] git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" @@ -1777,6 +1863,10 @@ deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" +[[deps.Profile]] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" +version = "1.11.0" + [[deps.ProgressMeter]] deps = ["Distributed", "Printf"] git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" @@ -1929,6 +2019,12 @@ git-tree-sha1 = "7cf3e4a9432defe1eaa9959c1c82e85485817d07" uuid = "cdeec39e-fb35-4959-aadb-a1dd5dede958" version = "2.0.1" +[[deps.SPRAL_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] +git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" +uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" +version = "2024.5.8+0" + [[deps.SQLite_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "004fffbe2711abdc7263a980bbb1af9620781dd9" @@ -1954,9 +2050,9 @@ version = "1.2.1" [[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "ff11acffdb082493657550959d4feb4b6149e73a" +git-tree-sha1 = "305becf8af67eae1dbc912ee9097f00aeeabb8d5" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.5" +version = "1.4.6" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -2075,9 +2171,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" +git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.7" +version = "1.9.8" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -2331,9 +2427,9 @@ version = "1.6.1" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" +git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.3+0" +version = "2.13.4+0" [[deps.XMLDict]] deps = ["EzXML", "IterTools", "OrderedCollections"] @@ -2349,9 +2445,9 @@ version = "1.1.41+0" [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" +git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.4.6+0" +version = "5.6.3+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -2421,9 +2517,9 @@ version = "0.7.3" [[deps.YAXArrays]] deps = ["CFTime", "DataStructures", "Dates", "DimensionalData", "DiskArrayTools", "DiskArrays", "Distributed", "DocStringExtensions", "Glob", "Interpolations", "IntervalSets", "IterTools", "Markdown", "OffsetArrays", "OnlineStats", "Optim", "OrderedCollections", "ParallelUtilities", "ProgressMeter", "Reexport", "Requires", "Statistics", "StatsBase", "Tables", "WeightedOnlineStats", "YAXArrayBase"] -git-tree-sha1 = "09cd01df4817c4d5f6eda845c4d75cc2855cf0b3" +git-tree-sha1 = "02d9149f67b26256ffa1e1c3915d474fae57099e" uuid = "c21b50f5-aa40-41ea-b809-c0f5e47bfa5c" -version = "0.5.11" +version = "0.5.12" [[deps.Zarr]] deps = ["AWSS3", "Blosc", "CodecZlib", "DataStructures", "Dates", "DiskArrays", "HTTP", "JSON", "LRUCache", "OffsetArrays", "OpenSSL", "Pkg", "URIs", "ZipArchives"] From 362e48c20d717831028ff69f01c98de86e01b163 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Mon, 28 Oct 2024 16:43:52 +1000 Subject: [PATCH 15/23] Add DEA example to analysis.md --- .../assets/imgs/analysis/example_dea_fig.png | Bin 0 -> 85105 bytes docs/src/usage/analysis.md | 34 ++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 docs/src/assets/imgs/analysis/example_dea_fig.png diff --git a/docs/src/assets/imgs/analysis/example_dea_fig.png b/docs/src/assets/imgs/analysis/example_dea_fig.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ca726898e1a596d39db91049749325f818c6d3 GIT binary patch literal 85105 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 literal 0 HcmV?d00001 diff --git a/docs/src/usage/analysis.md b/docs/src/usage/analysis.md index 9dc7ade13..862e628a0 100644 --- a/docs/src/usage/analysis.md +++ b/docs/src/usage/analysis.md @@ -490,6 +490,40 @@ save("outcome_map.png", tf) ![Outcome mapping](../assets/imgs/analysis/outcome_map.png) +### Data Envelopment Analysis + +Performs output-oriented (default, input-oriented can also be applied) Data Envelopment Analysis (DEA) +given inputs X and output metrics Y. DEA is used to measure the performance of entities (scenarios), +where inputs are converted to outputs via some process. Each scenario's "efficiency score" is calculated +relative to an "efficiency fromtier", a region representing scenarios for which outputs cannot be further +increased by changing inputs (scenario settings). + +```julia +dom = ADRIA.load_domain("path to domain", "45") + +scens = ADRIA.sample(dom, 128) +rs = ADRIA.run_scenarios(dom, scens, "45") + +n_scens = size(scens,1) + +# Get cost of deploying corals in each scenario, with user-specified function +cost = cost_function(scens) + +# Get mean coral cover and shelter volume for each scenario +s_tac = dropdims( + mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps +) +s_sv = dropdims( + mean(ADRIA.metrics.scenario_shelter_volume(rs); dims=:timesteps); dims=:timesteps + ) + +# Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum +# deployment cost. +DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, s_tac, s_sv) +dea_fig = ADRIA.viz.data_envelopment_analysis(rs, DEA_scens) + +![DEA](../assets/imgs/analysis/example_dea_fig.png) +``` ### GUI for high-level exploration (prototype only!) ```julia From 386c859f1954be25b5b24bb71ce850fbf64529da Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 6 Nov 2024 11:50:18 +1000 Subject: [PATCH 16/23] Add plotting and dea calculation to analysis tests --- test/analysis.jl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/analysis.jl b/test/analysis.jl index f1f2db360..2c829ca91 100644 --- a/test/analysis.jl +++ b/test/analysis.jl @@ -1,7 +1,7 @@ using WGLMakie, GeoMakie, GraphMakie using ADRIA using ADRIA.metrics: total_absolute_cover -using Statistics +using Statistics, YAXArrays, Distributions if !@isdefined(TEST_RS) const TEST_DOM, TEST_N_SAMPLES, TEST_SCENS, TEST_RS = test_rs() @@ -333,6 +333,27 @@ function test_rs_w_fig(rs::ADRIA.ResultSet, scens::ADRIA.DataFrame) )) # save("outcome_map.png", tf) + ## Data Envelopement Analysis + n_scens = size(scens,1) + + # Get cost of deploying corals in each scenario, with user-specified function + cost = YAXArray(collect(range(100,stop=1000000, length=n_scens)) .+ rand(Uniform(1000, 2000), n_scens)) + + # Get mean coral cover and shelter volume for each scenario + s_tac = dropdims( + mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps + ) + s_sv = + dropdims( + mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:locations); + dims=(:timesteps,:locations) + ) + + # Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum + # deployment cost. + DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, Array(s_tac), Array(s_sv)) + dea_fig = ADRIA.viz.data_envelopment_analysis(rs, DEA_scens) + return rs end From db9ebdd5dae4a040fc0ab74581f4433c746c841f Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 6 Nov 2024 15:44:36 +1000 Subject: [PATCH 17/23] Rename to data_envelopment.jl - fix function signatures and doc strings - remove constructor function as unnecessary - fix example in doc string - change Y type to AbstractArray - Allow Y to be Vector or Matrix in inner function' - Formatting --- src/analysis/analysis.jl | 1 + .../{economics.jl => data_envelopment.jl} | 116 +++++++----------- 2 files changed, 46 insertions(+), 71 deletions(-) rename src/analysis/{economics.jl => data_envelopment.jl} (58%) diff --git a/src/analysis/analysis.jl b/src/analysis/analysis.jl index ca6602065..d61876196 100644 --- a/src/analysis/analysis.jl +++ b/src/analysis/analysis.jl @@ -114,5 +114,6 @@ include("intervention.jl") include("clustering.jl") include("rule_extraction.jl") include("scenario.jl") +include("data_envelopment.jl") end diff --git a/src/analysis/economics.jl b/src/analysis/data_envelopment.jl similarity index 58% rename from src/analysis/economics.jl rename to src/analysis/data_envelopment.jl index 89914d1aa..026450e00 100644 --- a/src/analysis/economics.jl +++ b/src/analysis/data_envelopment.jl @@ -1,5 +1,3 @@ -module economics - using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA using ADRIA: ResultSet using DataFrames, YAXArrays @@ -16,41 +14,11 @@ struct DEAResult{V,V2,V3} end """ - DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, FDH_eff::Vector{Float64}, - CRS_peers::DEA.DEAPeers, VRS_peers::DEA.DEAPeers, FDH_peers::DEA.DEAPeers, - X::Matrix{Float64}, Y::Vector{Float64})::DEAResult - -Constructor for DEAResult type. - -# Arguments -- `CRS_eff` : efficiencies from CRS DEA analysis. -- `VRS_eff` : efficiencies from VRS DEA analysis. -- `FDH_eff` : efficiencies from FDH DEA analysis. -- `CRS_peers` : peers indices from CRS DEA analysis. -- `VRS_peers` : peers indices from VRS DEA analysis. -- `FDH_peers` : peers indices from FDH DEA analysis. -- `X` : inputs. -- `Y` : outputs. -""" -function DEAResult(CRS_eff::Vector{Float64}, VRS_eff::Vector{Float64}, - FDH_eff::Vector{Float64}, CRS_peers::DEA.DEAPeers, VRS_peers::DEA.DEAPeers, - FDH_peers::DEA.DEAPeers, X::Matrix{Float64}, Y::Vector{Float64} -)::DEAResult - return DEAResult(1 ./ CRS_eff, - 1 ./ VRS_eff, - 1 ./ FDH_eff, - CRS_peers, - VRS_peers, - FDH_peers, - X, - Y) -end - -""" - data_envelopment_analysis(X::YAXArray, Y::YAXArray; orient::Symbol=:Output, - dea_model::Function=DEA.deabigdata)::DEAResult - data_envelopment_analysis(X::YAXArray, metrics...; orient::Symbol=:Output, - dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(rs::ResultSet, input_function::Function, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(rs::ResultSet, Y::YAXArray, input_function::Function; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::YAXArray, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::YAXArray, Y::YAXArray; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::Union{Vector{Float64},Matrix{Float64}}, Y::Matrix{Float64}; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult Performs output-oriented (default) Data Envelopment Analysis (DEA) given inputs X and output metrics Y. DEA is used to measure the performance of entities (scenarios), where inputs are @@ -60,13 +28,16 @@ cannot be further increased by changing inputs (scenario settings). Scenarios on frontier serve as "benchmarks" or "peers", associated with best practice restoration scenarios. Scenarios with efficiencies not equal to 1 can be improved to be more efficient. - # Arguments +- `rs` : ADRIA ResultSet +- `input_function` : function which calculates an input for each scenario (e.g. cost, effort) given the scenario + dataframe as input. - `X` : Model inputs for each scenario (usually costs). - `Y` : Model outputs for each scenario (metrics such as tac, rci etc.). - `orient` : Orientation of the analysis. Can be output oriented (`orient=:Output`), which seeks to maximise outputs for a given level of input, or input oriented (`orient=:Input`), which seeks to minimise an input for a given level of output. +- `dea_model` : model to use to calculate DEA frontier (see https://javierbarbero.github.io/DataEnvelopmentAnalysis.jl/stable/) # Returns DEAResult, which summarizes inputs, outputs, efficiencies and peers for each scenario. @@ -84,18 +55,14 @@ cost = cost_function(scens) s_tac = dropdims( mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps ) -s_sv::Vector{Float64} = - dropdims( - mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:sites); - dims=(:timesteps, :sites) +s_sv = dropdims( + mean(ADRIA.metrics.scenario_shelter_volume(rs); dims=:timesteps); dims=:timesteps ) # Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum -# deployment cost. -DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, s_tac, s_sv) +# deployment cost.analysis.data_envelopment_analysis(cost, s_tac, s_sv) ``` - # References 1. Huguenin, J-M., 2012. Data Envelopment Analysis (DEA): A pedagogical guide for decision makers in the public @@ -109,61 +76,68 @@ DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, s_tac, s_sv) On the use of Data Envelopment Analysis for Multi-Criteria Decision Analysis. Algorithms, 17:89. https://doi.org/10.3390/a17030089 - """ -function data_envelopment_analysis(rs::ResultSet, input_function::Function, - metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +function data_envelopment_analysis( + rs::ResultSet, + input_function::Function, + metrics...; + orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata )::DEAResult - X = input_function(rs.inputs) return data_envelopment_analysis( - X, metrics; orient=orient, dea_model=dea_model + input_function(rs.inputs), metrics; orient=orient, dea_model=dea_model ) end -function data_envelopment_analysis(rs::ResultSet, Y::YAXArray, input_function::Function; - orient::Symbol=:Output, dea_model::Function=DEA.deabigdata +function data_envelopment_analysis( + rs::ResultSet, + Y::AbstractArray, + input_function::Function; + orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata )::DEAResult - X = input_function(rs.inputs) return data_envelopment_analysis( - X, Y; orient=orient, dea_model=dea_model + input_function(rs.inputs), Y; orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::YAXArray, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata + X::YAXArray, + metrics...; + orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata )::DEAResult Y = Array(hcat(metrics...)) return data_envelopment_analysis( - Array(X), Y; orient=orient, dea_model=dea_model + X.data, Y; orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::YAXArray, Y::YAXArray; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata + X::YAXArray, + Y::AbstractArray; + orient::Symbol=:Output, + dea_model::Function=DEA.deabigdata )::DEAResult return data_envelopment_analysis( - Array(X), Array(Y); orient=orient, dea_model=dea_model + X.data, Array(Y); orient=orient, dea_model=dea_model ) end function data_envelopment_analysis( - X::Vector{Float64}, Y::Matrix{Float64}; orient::Symbol=:Output, + X::Union{Vector{Float64},Matrix{Float64}}, + Y::Matrix{Float64}; + orient::Symbol=:Output, dea_model::Function=DEA.deabigdata )::DEAResult result_CRS = dea_model(X, Y; orient=orient, rts=:CRS) result_VRS = dea_model(X, Y; orient=orient, rts=:VRS) result_FDH = dea_model(X, Y; orient=orient, rts=:FDH) - CRS_peers = DEA.peers(result_CRS) - VRS_peers = DEA.peers(result_VRS) - FDH_peers = DEA.peers(result_FDH) - return DEAResult( - result_CRS.eff, - result_VRS.eff, - result_FDH.eff, - CRS_peers, - VRS_peers, - FDH_peers, + 1 ./ result_CRS.eff, + 1 ./ result_VRS.eff, + 1 ./ result_FDH.eff, + DEA.peers(result_CRS), + DEA.peers(result_VRS), + DEA.peers(result_FDH), X, Y ) end - -end From 838b1a996399b46337b61c6ea081e8877e9b9f65 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 6 Nov 2024 15:45:53 +1000 Subject: [PATCH 18/23] Rename viz file to data_analysis.jl - Formatting - adjust variable names - fix function signature and doc string --- .../viz/{economics.jl => data_envelopment.jl} | 34 +++++++++++-------- ext/AvizExt/viz/viz.jl | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) rename ext/AvizExt/viz/{economics.jl => data_envelopment.jl} (83%) diff --git a/ext/AvizExt/viz/economics.jl b/ext/AvizExt/viz/data_envelopment.jl similarity index 83% rename from ext/AvizExt/viz/economics.jl rename to ext/AvizExt/viz/data_envelopment.jl index d977abbee..a037c89d9 100644 --- a/ext/AvizExt/viz/economics.jl +++ b/ext/AvizExt/viz/data_envelopment.jl @@ -1,4 +1,4 @@ -using ADRIA.economics: DEAResult +using ADRIA.analysis: DEAResult """ ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult;axis_opts=Dict(), @@ -41,16 +41,20 @@ ADRIA.viz.data_envelopment_analysis(rs, DEA_output) # Arguments - `rs` : ResultSet - `DEA_output` : output structure from a DEA analysis, carried out using `data_envelopment_analysis` +- `axis_opts` : Additional options to pass to adjust Axis attributes + See: https://docs.makie.org/v0.19/api/index.html#Axis +- `fig_opts` : Additional options to pass to adjust Figure creation + See: https://docs.makie.org/v0.19/api/index.html#Figure - `opts` : Aviz options - `frontier_type`, type of frontier to plot: "CRS","VRS" or "FDH". - `line_color`, color to use for best practice frontier. - `data_color`, color to use for data cloud when plotting efficiency frontier. - `frontier_name`, text to label efficiency frontier. - `data_name`, text to label data cloud used to plot efficiency frontier. -- `axis_opts` : Additional options to pass to adjust Axis attributes - See: https://docs.makie.org/v0.19/api/index.html#Axis -- `fig_opts` : Additional options to pass to adjust Figure creation - See: https://docs.makie.org/v0.19/api/index.html#Figure + - `metrics_x_lab` : Name of metric on x axis. + - `metrics_y_lab` : Name of metric on y axis. + - `scale_eff_y_lab` : Label for scale efficiency. + - `scale_eff_y_lab` : Label for technical efficiency. """ function ADRIA.viz.data_envelopment_analysis( rs::ResultSet, DEA_output::DEAResult; @@ -98,28 +102,28 @@ function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, scale_efficiency = DEA_output.crs_vals ./ DEA_output.vrs_vals # Plot efficiency frontier and data cloud - axa = Axis(g[1, 1]; xlabel=metrics_x_lab, ylabel=metrics_y_lab, axis_opts...) - data = scatter!(axa, Y[:, 1], Y[:, 2]; color=data_color) + ax_a = Axis(g[1, 1]; xlabel=metrics_x_lab, ylabel=metrics_y_lab, axis_opts...) + data_cloud = scatter!(ax_a, Y[:, 1], Y[:, 2]; color=data_color) frontier = scatter!( - axa, Y[best_practice_scens, 1], Y[best_practice_scens, 2]; color=frontier_color + ax_a, Y[best_practice_scens, 1], Y[best_practice_scens, 2]; color=frontier_color ) - Legend(g[1, 2], [frontier, data], [frontier_name, data_name]) + Legend(g[1, 2], [frontier, data_cloud], [frontier_name, data_name]) # Plot the scale efficiency (ratio of efficiencies assuming CRS vs. assuming VRS) - axb = Axis(g[2, 1]; title="Scale efficiency", ylabel=scale_eff_y_lab, axis_opts...) - scatter!(axb, scale_efficiency; color=data_color) + ax_b = Axis(g[2, 1]; title="Scale efficiency", ylabel=scale_eff_y_lab, axis_opts...) + scatter!(ax_b, scale_efficiency; color=data_color) scatter!( - axb, + ax_b, best_practice_scens, scale_efficiency[best_practice_scens]; color=frontier_color ) # Plot the technical efficiency (inverse VRS efficiencies) - axc = Axis(g[3, 1]; title="Technical efficiency", ylabel=tech_eff_y_lab, axis_opts...) - scatter!(axc, DEA_output.vrs_vals; color=data_color) + ax_c = Axis(g[3, 1]; title="Technical efficiency", ylabel=tech_eff_y_lab, axis_opts...) + scatter!(ax_c, DEA_output.vrs_vals; color=data_color) scatter!( - axc, + ax_c, best_practice_scens, DEA_output.vrs_vals[best_practice_scens]; color=frontier_color diff --git a/ext/AvizExt/viz/viz.jl b/ext/AvizExt/viz/viz.jl index bb6f28556..ecd7cc025 100644 --- a/ext/AvizExt/viz/viz.jl +++ b/ext/AvizExt/viz/viz.jl @@ -102,4 +102,4 @@ include("spatial.jl") include("taxa_dynamics.jl") include("environment/dhw.jl") include("environment/cyclones.jl") -include("economics.jl") +include("data_envelopment.jl") From d39cbe2f96b9795517eb3cb0d1bcf6d987ab55e2 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 6 Nov 2024 15:47:13 +1000 Subject: [PATCH 19/23] Remove economics.jl from ADRIA - remove cf_difference_scenario for future PR --- src/ADRIA.jl | 1 - src/metrics/scenario.jl | 69 ----------------------------------------- test/analysis.jl | 21 ++++++++----- 3 files changed, 13 insertions(+), 78 deletions(-) diff --git a/src/ADRIA.jl b/src/ADRIA.jl index 2e3066abc..fb42809fb 100644 --- a/src/ADRIA.jl +++ b/src/ADRIA.jl @@ -77,7 +77,6 @@ include("metrics/performance.jl") include("scenario.jl") include("analysis/analysis.jl") include("analysis/sensitivity.jl") -include("analysis/economics.jl") include("ExtInterface/ADRIA/Domain.jl") include("ExtInterface/ReefMod/RMEDomain.jl") diff --git a/src/metrics/scenario.jl b/src/metrics/scenario.jl index bba823e16..485290cdf 100644 --- a/src/metrics/scenario.jl +++ b/src/metrics/scenario.jl @@ -282,72 +282,3 @@ function scenario_outcomes(rs::ResultSet, metrics::Vector{<:Metric})::YAXArray return scen_outcomes end - -""" - cf_difference_scenario(rs::ResultSet, metric::Function) - cf_difference_scenario(scens::DataFrame, outcomes::Union{YAXArray{Float64,3}, - YAXArray{Float64,2},YAXArray{Float32,3},YAXArray{Float32,2}}) - cf_difference_scenario( - scens::DataFrame, outcomes::Union{YAXArray{Float64,1},YAXArray{Float32,1}}) - -Get bootstrapped difference between each intervention and conterfactual scenarios. - -# Arguments -- `rs` : ResultSet -- `metric` : Function to use to calculate metric. -- `scens` : Scenario DataFrame -`outcomes` : YAXArray for a metric (can have 1-3 dimensions which must include :scenarios) - -# Returns -YAXArray with (:timesteps, :scenarios, :outcomes) - -# Examples -``` -metrics::Vector{ADRIA.metrics.Metric} = [ - ADRIA.metrics.scenario_total_cover, - ADRIA.metrics.scenario_asv, - ADRIA.metrics.scenario_absolute_juveniles, -] - -# 3-dimensional Array of outcomes -outcomes = ADRIA.metrics.scenario_outcomes(rs, metrics) -``` -""" -function cf_difference_scenario(rs::ResultSet, metric::Function) - scens = rs.outputs # Get scenario dataframe - outcomes = metric(rs) # Extract some metric - return cf_difference_scenario(scens, outcomes) -end -function cf_difference_scenario( - scens::DataFrame, - outcomes::Union{ - YAXArray{Float64,3},YAXArray{Float64,2},YAXArray{Float32,3},YAXArray{Float32,2} - } -) - # Get metric mean over all dims except scenarios - outcome_dims = axes_names(outcomes)[findall(axes_names(outcomes) .!= :scenarios)] - outcomes_mean = dropdims(mean(outcomes; dims=outcome_dims); dims=outcome_dims) - return cf_difference_scenario(scens, outcomes_mean) -end -function cf_difference_scenario( - scens::DataFrame, outcomes::Union{YAXArray{Float64,1},YAXArray{Float32,1}} -) - itv_scenarios = findall(scens.guided .>= 0) # All intervention scenario IDs - cf_scenarios = findall(scens.guided .== -1) # All counterfactual scenario IDs - result = ZeroDataCube(; T=Float64, itv_scenarios=itv_scenarios) # Storage - cf_outcomes = outcomes[scenarios=cf_scenarios] - itv_outcomes = outcomes[scenarios=itv_scenarios] - - n_scens_itv = length(itv_scenarios) - n_scens_cf = length(cf_scenarios) - - for scen in 1:n_scens_itv - cf_shuf_set = shuffle(1:n_scens_cf) # shuffle counterfactual scenarios - itv_diff = collect(itv_outcomes[scen] .- cf_outcomes[cf_shuf_set]) - - # bootstap difference between an intervention scenario and counterfactuals - result[scen] = bootstrap(median, itv_diff, BalancedSampling(100)).t0[1] - end - - return result -end diff --git a/test/analysis.jl b/test/analysis.jl index 2c829ca91..21fd1aaab 100644 --- a/test/analysis.jl +++ b/test/analysis.jl @@ -334,24 +334,29 @@ function test_rs_w_fig(rs::ADRIA.ResultSet, scens::ADRIA.DataFrame) # save("outcome_map.png", tf) ## Data Envelopement Analysis - n_scens = size(scens,1) + n_scens = size(scens, 1) # Get cost of deploying corals in each scenario, with user-specified function - cost = YAXArray(collect(range(100,stop=1000000, length=n_scens)) .+ rand(Uniform(1000, 2000), n_scens)) + cost = YAXArray( + collect(range(100; stop=1000000, length=n_scens)) .+ + rand(Uniform(1000, 2000), n_scens) + ) # Get mean coral cover and shelter volume for each scenario s_tac = dropdims( mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps ) - s_sv = - dropdims( - mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:locations); - dims=(:timesteps,:locations) - ) + s_sv = dropdims( + mean( + mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); + dims=:locations + ); + dims=(:timesteps, :locations) + ) # Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum # deployment cost. - DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, Array(s_tac), Array(s_sv)) + DEA_scens = ADRIA.analysis.data_envelopment_analysis(cost, s_tac, s_sv) dea_fig = ADRIA.viz.data_envelopment_analysis(rs, DEA_scens) return rs From 89f650b5697b3d1f32648b3af743b4a074a4f3cd Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 6 Nov 2024 15:47:34 +1000 Subject: [PATCH 20/23] Update example in analysis.md --- docs/src/usage/analysis.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/usage/analysis.md b/docs/src/usage/analysis.md index 862e628a0..654cd5d34 100644 --- a/docs/src/usage/analysis.md +++ b/docs/src/usage/analysis.md @@ -513,13 +513,15 @@ cost = cost_function(scens) s_tac = dropdims( mean(ADRIA.metrics.scenario_total_cover(rs); dims=:timesteps); dims=:timesteps ) -s_sv = dropdims( - mean(ADRIA.metrics.scenario_shelter_volume(rs); dims=:timesteps); dims=:timesteps +s_sv = + dropdims( + mean(mean(ADRIA.metrics.absolute_shelter_volume(rs); dims=:timesteps); dims=:locations); + dims=(:timesteps,:locations) ) # Do output oriented DEA analysis seeking to maximise cover and shelter volume for minimum # deployment cost. -DEA_scens = ADRIA.economics.data_envelopment_analysis(cost, s_tac, s_sv) +DEA_scens = ADRIA.analysis.data_envelopment_analysis(cost, s_tac, s_sv) dea_fig = ADRIA.viz.data_envelopment_analysis(rs, DEA_scens) ![DEA](../assets/imgs/analysis/example_dea_fig.png) From a1c92cdea570470a989edf2e63229cbb2eeff635 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 12 Nov 2024 16:01:00 +1000 Subject: [PATCH 21/23] Formatting and fix function signatures --- ext/AvizExt/viz/data_envelopment.jl | 22 ++++++++++++---------- src/analysis/data_envelopment.jl | 6 +++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ext/AvizExt/viz/data_envelopment.jl b/ext/AvizExt/viz/data_envelopment.jl index a037c89d9..be1e5e84b 100644 --- a/ext/AvizExt/viz/data_envelopment.jl +++ b/ext/AvizExt/viz/data_envelopment.jl @@ -1,12 +1,9 @@ using ADRIA.analysis: DEAResult """ - ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult;axis_opts=Dict(), - fig_opts=Dict(), opts=Dict()) - ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition},rs::ResultSet, - DEA_output::DEAResult; axis_opts=Dict(),opts=Dict()) - ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, DEA_output::DEAResult; - axis_opts=Dict(), opts=Dict()) + ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), fig_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE()) + ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, rs::ResultSet, DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE()) + ADRIA.viz.data_envelopment_analysis(g::Union{GridLayout,GridPosition}, DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE()) Plot results from a DEA analysis. Plots the first 2 dimensions of the effificency frontier, along side the technical and scale efficiencies. @@ -57,8 +54,10 @@ ADRIA.viz.data_envelopment_analysis(rs, DEA_output) - `scale_eff_y_lab` : Label for technical efficiency. """ function ADRIA.viz.data_envelopment_analysis( - rs::ResultSet, DEA_output::DEAResult; - axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), fig_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), + rs::ResultSet, + DEA_output::DEAResult; + axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), + fig_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE() ) f = Figure(; fig_opts...) @@ -70,8 +69,11 @@ function ADRIA.viz.data_envelopment_analysis( return f end -function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, - rs::ResultSet, DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), +function ADRIA.viz.data_envelopment_analysis!( + g::Union{GridLayout,GridPosition}, + rs::ResultSet, + DEA_output::DEAResult; + axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE() ) return ADRIA.viz.data_envelopment_analysis!( diff --git a/src/analysis/data_envelopment.jl b/src/analysis/data_envelopment.jl index 026450e00..e677ef22f 100644 --- a/src/analysis/data_envelopment.jl +++ b/src/analysis/data_envelopment.jl @@ -15,10 +15,10 @@ end """ data_envelopment_analysis(rs::ResultSet, input_function::Function, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult - data_envelopment_analysis(rs::ResultSet, Y::YAXArray, input_function::Function; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(rs::ResultSet, Y::AbstractArray, input_function::Function; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult data_envelopment_analysis(X::YAXArray, metrics...; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult - data_envelopment_analysis(X::YAXArray, Y::YAXArray; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult - data_envelopment_analysis(X::Union{Vector{Float64},Matrix{Float64}}, Y::Matrix{Float64}; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::YAXArray, Y::AbstractArray; orient::Symbol=:Output, dea_model::Function=DEA.deabigdata)::DEAResult + data_envelopment_analysis(X::Union{Vector{Float64}, Matrix{Float64}}, Y::Matrix{Float64}; dea_model::Function=DEA.deabigdata)::DEAResult Performs output-oriented (default) Data Envelopment Analysis (DEA) given inputs X and output metrics Y. DEA is used to measure the performance of entities (scenarios), where inputs are From 1cb5028ea58a2e64c1c9496cdd7a4579d02883a5 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Thu, 12 Dec 2024 10:27:22 +1000 Subject: [PATCH 22/23] Fold legend creation into separate function`_render_marker_legend` --- Project.toml | 1 + ext/AvizExt/viz/data_envelopment.jl | 43 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index d005a4098..3b5b2fa9e 100644 --- a/Project.toml +++ b/Project.toml @@ -86,6 +86,7 @@ Clustering = "0.15" Combinatorics = "1" CoralBlox = "1" CpuId = "0.3" +DataEnvelopmentAnalysis = "0.9.1" DataFrames = "1" DataStructures = "0.18, 0.2" Dates = "1" diff --git a/ext/AvizExt/viz/data_envelopment.jl b/ext/AvizExt/viz/data_envelopment.jl index be1e5e84b..ef752e930 100644 --- a/ext/AvizExt/viz/data_envelopment.jl +++ b/ext/AvizExt/viz/data_envelopment.jl @@ -80,13 +80,15 @@ function ADRIA.viz.data_envelopment_analysis!( g, DEA_output; opts=opts, axis_opts=axis_opts ) end -function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, - DEA_output::DEAResult; axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), +function ADRIA.viz.data_envelopment_analysis!( + g::Union{GridLayout,GridPosition}, + DEA_output::DEAResult; + axis_opts::OPT_TYPE=DEFAULT_OPT_TYPE(), opts::OPT_TYPE=DEFAULT_OPT_TYPE()) - frontier_color = get(opts, :frontier_color, :red) - data_color = get(opts, :data_color, :black) - frontier_name = get(opts, :frontier_name, "Best practice frontier") - data_name = get(opts, :data_name, "Scenario data cloud") + scatter_colors = get(opts, :scatter_colors, [:red, :black]) + legend_names = get( + opts, :legend_names, ["Best practice frontier", "Scenario data cloud"] + ) scale_eff_y_lab = get(opts, :scale_eff_y_lab, L"$\frac{eff_{vrs}}{eff_{crs}}$") tech_eff_y_lab = get(opts, :tech_eff_y_lab, L"$\frac{1}{eff_{vrs}}$") metrics_x_lab = get(opts, :metrics_x_lab, L"$metric 1$") @@ -105,31 +107,44 @@ function ADRIA.viz.data_envelopment_analysis!(g::Union{GridLayout,GridPosition}, # Plot efficiency frontier and data cloud ax_a = Axis(g[1, 1]; xlabel=metrics_x_lab, ylabel=metrics_y_lab, axis_opts...) - data_cloud = scatter!(ax_a, Y[:, 1], Y[:, 2]; color=data_color) - frontier = scatter!( - ax_a, Y[best_practice_scens, 1], Y[best_practice_scens, 2]; color=frontier_color + scatter!(ax_a, Y[:, 1], Y[:, 2]; color=scatter_colors[2]) + scatter!( + ax_a, Y[best_practice_scens, 1], Y[best_practice_scens, 2]; color=scatter_colors[1] ) - Legend(g[1, 2], [frontier, data_cloud], [frontier_name, data_name]) + _render_marker_legend(g, (1, 2), legend_names, scatter_colors) # Plot the scale efficiency (ratio of efficiencies assuming CRS vs. assuming VRS) ax_b = Axis(g[2, 1]; title="Scale efficiency", ylabel=scale_eff_y_lab, axis_opts...) - scatter!(ax_b, scale_efficiency; color=data_color) + scatter!(ax_b, scale_efficiency; color=scatter_colors[2]) scatter!( ax_b, best_practice_scens, scale_efficiency[best_practice_scens]; - color=frontier_color + color=scatter_colors[1] ) # Plot the technical efficiency (inverse VRS efficiencies) ax_c = Axis(g[3, 1]; title="Technical efficiency", ylabel=tech_eff_y_lab, axis_opts...) - scatter!(ax_c, DEA_output.vrs_vals; color=data_color) + scatter!(ax_c, DEA_output.vrs_vals; color=scatter_colors[2]) scatter!( ax_c, best_practice_scens, DEA_output.vrs_vals[best_practice_scens]; - color=frontier_color + color=scatter_colors[1] ) return g end + +function _render_marker_legend(g::Union{GridLayout,GridPosition}, + legend_position::Tuple{Int64,Int64}, + legend_labels::Union{Vector{Symbol},Vector{String}}, + colors::Union{Vector{Symbol},RGBA{Float32}} +)::Nothing + marker_els::Vector{MarkerElement} = [ + MarkerElement(; color=color, marker=:circle) for color in colors + ] + Legend(g[legend_position...], marker_els, legend_labels; framevisible=false) + + return nothing +end From 382c4b90c3044fcea78eb17cdb5a60ae3764ea37 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Thu, 12 Dec 2024 11:56:50 +1000 Subject: [PATCH 23/23] Update julia version --- Manifest-v1.11.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Manifest-v1.11.toml b/Manifest-v1.11.toml index 36101ba76..8e53ab148 100644 --- a/Manifest-v1.11.toml +++ b/Manifest-v1.11.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.0" +julia_version = "1.11.2" manifest_format = "2.0" -project_hash = "6ea7e541a27709a6f9e32ca4b0816717a46eb307" +project_hash = "4ffb592df61e4d217c5b2589556288d921d4ba0f" [[deps.ARFFFiles]] deps = ["CategoricalArrays", "Dates", "Parsers", "Tables"]