Code
rm(list=ls())
library(tidyverse)
options(dplyr.summarise.inform = FALSE)
library(scales)
library(rlang)
library(gt)
library(RColorBrewer)
library(forcats)
library(magrittr)
library(webshot2)
This conducts cost-effectiveness analysis and produces the main results tables charts used in the Results section of the report.
Open libraries
rm(list=ls())
library(tidyverse)
options(dplyr.summarise.inform = FALSE)
library(scales)
library(rlang)
library(gt)
library(RColorBrewer)
library(forcats)
library(magrittr)
library(webshot2)
Load functions and assumptions
#Useful monte carlo functions
source("a_functions.R")
#Take on board assupmtions for seabream, seabass and trout
source("b_fish_assumptions.R")
#Modelling assumptions
source("c_model_assumptions.R")
Write helper functions to call object/vectors
<- function(country,species) obtain(species,country,"tons")
consumption <- function(scenario,country) obtain("mshare",scenario,country)
mshare <- function(scenario) obtain("psuccess",scenario)
psuccess <- function(species) obtain("weight",species)
weight <- function(species) obtain("slaughter_minutes",species)
slaughter_minutes <- function(species) obtain(species,"stunned")
stun_share <- function(scenario) obtain("cost",scenario) cost
# Number of fish affected per year
<- function(country,species,scenario) {
no_fish_affected_per_year consumption(country,species)*1E3/
weight(species)*
1-stun_share(species))*
(mshare(scenario,country)*
psuccess(scenario)*
*
implementation_discount
fish_grocery
}
# Fish affected across all years of credit
<- function(country,species,scenario) {
no_fish_affected no_fish_affected_per_year(country,species,scenario)*years_credit
}
# Fish minutes affected
<- function(country,species,scenario) {
fish_hours_affected no_fish_affected(country,species,scenario)*slaughter_minutes(species)/60
}
I now produce a table that produces a possible combination of country and species
<- length(country_list)
no_of_countries <- expand.grid(country=country_list,species=species_list) %>%
combo mutate(country_species=str_c(country,"_",species))
I now write a function that:
Works out the total number of fish (or fish minutes) affected in a given scenario, country and species
Sums the result for all species and countries
Divides by the total program costs for a specific scenario
<- function(fish_function,program) {
output_per_dollar
<- map2( # Allows repeated calcs across different input vectors
temp_output$country, # Input vector 1: countries to run function over
combo$species, # Input vector 2: species to run function over
comboget(fish_function), # Calculation function (no of fish or minutes)
scenario=program # Scenario (pilot or scale)
%>%
) as.data.frame() %>% # Convert to data.frame
mutate(
total=rowSums(.))
return(
$total/(no_of_countries*cost(program))
temp_output
) }
Generate table for combinations of output and scenario
<- expand_grid(
results_combination measure=c("fish_hours_affected","no_fish_affected","no_fish_affected_per_year"),
program=c("pilot","scale")) %>%
mutate(description=str_c(program,"___",measure))
I now write a function that works out the number of fish minutes affected and total number of fish affected per dollar across both the pilot and scale scenarios. The results of the montecarlo simulation are in a single table called “impact_per_dollar”.
<- map2( # Function to iterate over vectors
impact_per_dollar $measure, # Vector of different measures
results_combination$program, # Vector of different scenarios
results_combination%>% # Function to repeat
output_per_dollar) as.data.frame()
# Assign names to data.frame
names(impact_per_dollar) <- results_combination$description
# Convert to long format
%<>%
impact_per_dollar pivot_longer(
cols=1:nrow(results_combination),
names_to="description",
values_to="number"
%>%
) separate_wider_delim(description,delim="___",names=c("scenario","measure"))
I first produce estimates of fish years affected per dollar in both scenarios.
<- output_per_dollar("fish_hours_affected","pilot")*hours_to_years
fypd_pilot <- output_per_dollar("fish_hours_affected","scale")*hours_to_years
fypd_scale <- function(scenario) obtain("fypd",scenario) fypd
<- function(scenario) {
wr_dollar_per_daly 1/(
fypd(scenario)*
*
salmon_wr*
duration_share/
fish_welfarerange_impact_stun
DALY_share_of_welfare_range) }
Calculate results and place into data frame
<-
results_dollar_per_daly tibble(
# core calculations
pilot=wr_dollar_per_daly("pilot"),
scale=wr_dollar_per_daly("scale")) %>%
# pivot to long format
pivot_longer(
cols=1:2,
values_to="dollars_per_daly",
names_to="scenario"
%>%
) # reformat for table production
mutate(scenario=str_to_sentence(scenario))
# Code to create impact per dollar density plot
<- function(desired_filter,description) {
impact_density_plot %>%
impact_per_dollar filter(measure==desired_filter) %>%
ggplot(
aes(
x=number,
color=str_to_sentence(scenario)
)+
) geom_density() +
theme_light() +
scale_x_log10(labels = scales::comma_format(drop0trailing = TRUE),n.breaks=8) +
labs(
title=description,
y="Probability density",
x=paste0(description," (log scale)"),
color="Scenario")
}
<- function(desired_filter,description) {
impact_table %>%
impact_per_dollar filter(measure==desired_filter) %>%
mutate(scenario=str_to_sentence(scenario)) %>%
group_by(scenario) %>%
summarise(
Mean=mean(number),
p5=quantile(number,0.05),
Median=median(number),
p95=quantile(number,0.95)
%>%
) pivot_longer(cols=2:5,names_to="Statistic",values_to="Value") %>%
pivot_wider(names_from = "scenario",values_from = "Value") %>%
gt() %>%
fmt_number(decimals = 1) %>%
tab_header(
title = description) %>%
tab_spanner(
label = "Scenario",
columns = c(Pilot,Scale))
}
<- impact_density_plot("no_fish_affected","Number of fish affected per dollar")
fig9_fish_pd <- impact_table("no_fish_affected","Number of fish affected per dollar")
tab8_fish_pd<- impact_table("no_fish_affected_per_year","Number of fish affected per dollar per year")
tab9_fish_pdpy <- impact_density_plot("fish_hours_affected","Fish hours affected per dollar")
fig10_fish_hours_pd <- impact_table("fish_hours_affected","Fish hours affected per dollar") tab10_fish_hours_pd
<-
fig11_dolperdaly_density %>%
results_dollar_per_daly ggplot(
aes(
x=dollars_per_daly,
color=scenario
)+
) geom_density() +
theme_light() +
scale_x_log10(
breaks=10^seq(0,15,1),
labels = scales::label_number(scale_cut = scales::cut_short_scale())) +
labs(
title = "$/DALY Density Plot",
y="Probability density",
x="$/DALY (log-scale)",
color="Scenario")
<-
tab11_dolperdaly_density %>%
results_dollar_per_daly group_by(scenario) %>%
summarise(
Mean=mean(dollars_per_daly),
p1=quantile(dollars_per_daly,0.01),
p5=quantile(dollars_per_daly,0.05),
p10=quantile(dollars_per_daly,0.10),
p25=quantile(dollars_per_daly,0.25),
Median=median(dollars_per_daly),
p75=quantile(dollars_per_daly,0.75),
p90=quantile(dollars_per_daly,0.90),
p95=quantile(dollars_per_daly,0.95),
p99=quantile(dollars_per_daly,0.99)
%>%
) pivot_longer(cols=2:11,names_to="Statistic",values_to="$/DALY") %>%
pivot_wider(names_from = "scenario",values_from = "$/DALY") %>%
gt() %>%
tab_spanner(
label = "Scenario",
columns = c(Pilot,Scale)) %>%
fmt_number(columns=2:3,suffixing=TRUE,n_sigfig =3)
<-
tab11_supplement %>%
results_dollar_per_daly mutate(
`$50/DALY`=dollars_per_daly<50,
`$1K/DALY`=dollars_per_daly<1000,
`$70K/DALY`=dollars_per_daly<70000) %>%
group_by(scenario) %>%
summarise_if(is.logical,sum,na.rm=TRUE) %>%
gt() %>%
tab_header(
title = "Number of simulations beating selected $/DALY benchmarks")
Impact per dollar charts and tables
fig9_fish_pd
tab8_fish_pd
Number of fish affected per dollar | ||
Statistic | Scenario | |
---|---|---|
Pilot | Scale | |
Mean | 4.6 | 13.3 |
p5 | 0.6 | 1.9 |
Median | 3.2 | 9.9 |
p95 | 13.5 | 36.0 |
tab9_fish_pdpy
Number of fish affected per dollar per year | ||
Statistic | Scenario | |
---|---|---|
Pilot | Scale | |
Mean | 0.4 | 1.2 |
p5 | 0.1 | 0.2 |
Median | 0.3 | 1.0 |
p95 | 1.1 | 2.8 |
fig10_fish_hours_pd
tab10_fish_hours_pd
Fish hours affected per dollar | ||
Statistic | Scenario | |
---|---|---|
Pilot | Scale | |
Mean | 1.2 | 3.6 |
p5 | 0.1 | 0.4 |
Median | 0.8 | 2.5 |
p95 | 3.8 | 10.4 |
Dollar per daly distribution
fig11_dolperdaly_density
tab11_dolperdaly_density
Statistic | Scenario | |
---|---|---|
Pilot | Scale | |
Mean | 41.1B | 12.3B |
p1 | 11.8K | 4.11K |
p5 | 29.9K | 10.4K |
p10 | 52.3K | 17.8K |
p25 | 151K | 51.0K |
Median | 680K | 216K |
p75 | 4.92M | 1.61M |
p90 | 61.0M | 18.1M |
p95 | 366M | 114M |
p99 | 26.9B | 9.98B |
tab11_supplement
Number of simulations beating selected $/DALY benchmarks | |||
scenario | $50/DALY | $1K/DALY | $70K/DALY |
---|---|---|---|
Pilot | 0 | 0 | 1353 |
Scale | 0 | 3 | 3078 |
Welfare range benchmarks
fig12_wr_benchmark
tab12_wr_benchmark
Share of simulations beating $DALY bar | ||||||
Welfare range | Pilot_50 | Pilot_1000 | Pilot_70000 | Scale_50 | Scale_1000 | Scale_70000 |
---|---|---|---|---|---|---|
1% | 0.0% | 0.0% | 0.0% | 0.0% | 0.0% | 0.4% |
5% | 0.0% | 0.0% | 1.8% | 0.0% | 0.0% | 10.3% |
10% | 0.0% | 0.0% | 5.9% | 0.0% | 0.0% | 20.0% |
25% | 0.0% | 0.0% | 17.5% | 0.0% | 0.0% | 37.0% |
50% | 0.0% | 0.0% | 29.0% | 0.0% | 0.2% | 49.2% |
75% | 0.0% | 0.0% | 36.1% | 0.0% | 0.5% | 55.5% |
90% | 0.0% | 0.0% | 39.4% | 0.0% | 0.9% | 58.2% |
95% | 0.0% | 0.0% | 40.4% | 0.0% | 1.0% | 59.1% |
99% | 0.0% | 0.0% | 41.2% | 0.0% | 1.1% | 59.6% |
100% | 0.0% | 0.0% | 41.4% | 0.0% | 1.2% | 59.8% |
Moral value benchmarks
fig13_mv_benchmark
tab13_mv_benchmark
Share of simulations beating $DALY bar | ||||||
Moral value | Pilot_50 | Pilot_1000 | Pilot_70000 | Scale_50 | Scale_1000 | Scale_70000 |
---|---|---|---|---|---|---|
0.01 | 0.0% | 0.0% | 0.1% | 0.0% | 0.0% | 2.9% |
0.10 | 0.0% | 0.0% | 32.6% | 0.0% | 0.0% | 74.7% |
0.25 | 0.0% | 0.0% | 66.8% | 0.0% | 0.1% | 93.3% |
0.50 | 0.0% | 0.0% | 85.3% | 0.0% | 1.0% | 97.8% |
0.75 | 0.0% | 0.1% | 91.5% | 0.0% | 3.5% | 99.0% |
1.00 | 0.0% | 0.4% | 94.7% | 0.0% | 7.6% | 99.6% |
5.00 | 0.0% | 21.1% | 99.7% | 0.1% | 63.4% | 100.0% |
10.00 | 0.0% | 46.1% | 100.0% | 1.0% | 84.6% | 100.0% |
25.00 | 0.8% | 77.7% | 100.0% | 12.2% | 96.3% | 100.0% |
50.00 | 6.1% | 91.1% | 100.0% | 35.3% | 98.9% | 100.0% |
75.00 | 13.2% | 95.3% | 100.0% | 52.1% | 99.6% | 100.0% |
100.00 | 21.1% | 97.2% | 100.0% | 63.4% | 99.8% | 100.0% |
500.00 | 77.7% | 99.9% | 100.0% | 96.3% | 100.0% | 100.0% |
1000.00 | 91.1% | 100.0% | 100.0% | 98.9% | 100.0% | 100.0% |
5000.00 | 99.5% | 100.0% | 100.0% | 100.0% | 100.0% | 100.0% |
tab14_min_mv
Minimum moral value assumption needed for to achieve share of simulations beating bar | ||||||
sims_bin | Pilot_50 | Pilot_1000 | Pilot_70000 | Scale_50 | Scale_1000 | Scale_70000 |
---|---|---|---|---|---|---|
0% | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1% | 26.9 | 1.3 | 0.0 | 10.0 | 0.5 | 0.0 |
10% | 64.6 | 3.2 | 0.0 | 22.9 | 1.1 | 0.0 |
25% | 114.8 | 5.6 | 0.1 | 38.9 | 1.9 | 0.0 |
50% | 223.9 | 11.2 | 0.2 | 72.4 | 3.6 | 0.1 |
75% | 457.1 | 22.9 | 0.3 | 144.5 | 7.1 | 0.1 |
90% | 933.3 | 46.8 | 0.7 | 275.4 | 13.8 | 0.2 |
95% | 1,479.1 | 74.1 | 1.0 | 426.6 | 21.4 | 0.3 |
99% | 3,467.4 | 173.8 | 2.5 | 1,023.3 | 51.3 | 0.7 |
100% | NA | 1,122.0 | 16.2 | 6,456.5 | 323.6 | 4.7 |
save_chart("fig9_fish_pd.png",fig9_fish_pd)
save_chart("fig10_fish_hours_pd.png",fig10_fish_hours_pd)
save_chart("fig11_dolperdaly_density.png",fig11_dolperdaly_density)
save_chart("fig12_wr_benchmark.png",fig12_wr_benchmark)
save_chart("fig13_mv_benchmark.png",fig13_mv_benchmark)
save_chart("fig13_mv_benchmark.png",fig13_mv_benchmark)
save_table(tab8_fish_pd,"tab8_fish_pd.html")
save_table(tab9_fish_pdpy,"tab9_fish_pdpy.html")
save_table(tab10_fish_hours_pd,"tab10_fish_hours_pd.html")
save_table(tab11_dolperdaly_density,"tab11_dolperdaly_density.html")
save_table(tab12_wr_benchmark,"tab12_wr_benchmark.html")
save_table(tab13_mv_benchmark ,"tab13_mv_benchmark.html")
save_table(tab14_min_mv,"tab14_min_mv.html")
saveRDS(fypd_scale,file= "../3_intermediate_data/fypd_scale.rds")
I this section I perform a quick BOTEC to see how the animals affected per dollar from the fish stunning intervention in the scale scenario might compare to marginal chicken grants.
I first import raw sample data (5000 simulations) for chickens affected per dollar from the Guesstimate model from Šimčikas (2019).
# Import data on chickens affected per dollar from Guesstimate samples
<-
chickens_per_dollar_historic read_csv("../1_input_data/chickens_per_dollar.csv",show_col_types=FALSE) %>%
pull(chickens_per_dollar) %>%
sample(sims,replace=TRUE)
I then estimate marginal chickens affected per dollar, conditional on an assumption that marginal grants may impact 3.5 to 5 times fewer animals per dollar (uniform distribution), than historic ones.
# Assumption about guesstimate samples
<- runi(3.5,5)
decrease_in_chicken_effectiveness <- chickens_per_dollar_historic/decrease_in_chicken_effectiveness chickens_per_dollar_marginal
I then work out the share of simulations where marginal chicken grants affect more animals per dollar then fish stunning grants in the scale scenario (about half the time)
# Assumption about guesstimate samples
mean(chickens_per_dollar_marginal>output_per_dollar("no_fish_affected","scale"))
[1] 0.4933
I then look at how the overall distributions compare (look comparable)
summarystats(chickens_per_dollar_marginal,output_per_dollar("no_fish_affected","scale"))
statistic | chickens_per_dollar_marginal | output_per_dollar("no_fish_affected", "scale") |
---|---|---|
mean | 15.0 | 13.3 |
sd | 17.6 | 11.8 |
median | 9.14 | 9.90 |
p5 | 2.15 | 1.89 |
p10 | 2.90 | 2.89 |
p25 | 4.91 | 5.37 |
p75 | 18.0 | 17.4 |
p90 | 33.3 | 27.9 |
p95 | 48.4 | 36.0 |