Skip to content

Commit

Permalink
Changes to make DEA agnostic to input/output functions
Browse files Browse the repository at this point in the history
Fix typos, remove cost function from input to `data_envelopment_analysis`
  • Loading branch information
Rosejoycrocker committed Oct 28, 2024
1 parent cec4ddb commit 88cffb0
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 123 deletions.
83 changes: 47 additions & 36 deletions ext/AvizExt/viz/economics.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using ADRIA: DEAResult
using ADRIA.economics: DEAResult

"""
ADRIA.viz.data_envelopment_analysis(rs::ResultSet, DEA_output::DEAResult;axis_opts=Dict(),
Expand All @@ -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
Expand All @@ -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)
```
Expand All @@ -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
2 changes: 0 additions & 2 deletions src/analysis/deploy_cap_cost.csv

This file was deleted.

5 changes: 0 additions & 5 deletions src/analysis/deploy_op_cost.csv

This file was deleted.

97 changes: 17 additions & 80 deletions src/analysis/economics.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#module economics
module economics

using CSV
using DataEnvelopmentAnalysis: DataEnvelopmentAnalysis as DEA
using BasicInterpolators
using ADRIA: ResultSet
using DataFrames, YAXArrays

Expand All @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -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)
```
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand All @@ -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
1 change: 1 addition & 0 deletions src/viz/viz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ function taxonomy!() end

# Economics
function data_envelopment_analysis() end
function data_envelopment_analysis!() end

end # module

0 comments on commit 88cffb0

Please sign in to comment.