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
consumption <- function(country,species) obtain(species,country,"tons")
mshare <- function(scenario,country) obtain("mshare",scenario,country)
psuccess <- function(scenario) obtain("psuccess",scenario)
weight <- function(species) obtain("weight",species)
slaughter_minutes <- function(species) obtain("slaughter_minutes",species)
stun_share <- function(species) obtain(species,"stunned")
cost <- function(scenario) obtain("cost",scenario)# Number of fish affected per year
no_fish_affected_per_year <- function(country,species,scenario) {
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
no_fish_affected <- function(country,species,scenario) {
no_fish_affected_per_year(country,species,scenario)*years_credit
}
# Fish minutes affected
fish_hours_affected <- function(country,species,scenario) {
no_fish_affected(country,species,scenario)*slaughter_minutes(species)/60
}I now produce a table that produces a possible combination of country and species
no_of_countries <- length(country_list)
combo <- expand.grid(country=country_list,species=species_list) %>%
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
output_per_dollar <- function(fish_function,program) {
temp_output<- map2( # Allows repeated calcs across different input vectors
combo$country, # Input vector 1: countries to run function over
combo$species, # Input vector 2: species to run function over
get(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(
temp_output$total/(no_of_countries*cost(program))
)
}Generate table for combinations of output and scenario
results_combination <- expand_grid(
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”.
impact_per_dollar <- map2( # Function to iterate over vectors
results_combination$measure, # Vector of different measures
results_combination$program, # Vector of different scenarios
output_per_dollar) %>% # Function to repeat
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.
fypd_pilot <- output_per_dollar("fish_hours_affected","pilot")*hours_to_years
fypd_scale <- output_per_dollar("fish_hours_affected","scale")*hours_to_years
fypd <- function(scenario) obtain("fypd",scenario)wr_dollar_per_daly <- function(scenario) {
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
impact_density_plot <- function(desired_filter,description) {
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")
}impact_table <- function(desired_filter,description) {
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))
}fig9_fish_pd <- impact_density_plot("no_fish_affected","Number of fish affected per dollar")
tab8_fish_pd<- impact_table("no_fish_affected","Number of fish affected per dollar")
tab9_fish_pdpy <- impact_table("no_fish_affected_per_year","Number of fish affected per dollar per year")
fig10_fish_hours_pd <- impact_density_plot("fish_hours_affected","Fish hours affected per dollar")
tab10_fish_hours_pd <- impact_table("fish_hours_affected","Fish hours affected per dollar")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_pdtab8_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_pdtab10_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_densitytab11_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_benchmarktab12_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_benchmarktab13_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
decrease_in_chicken_effectiveness <- runi(3.5,5)
chickens_per_dollar_marginal <- chickens_per_dollar_historic/decrease_in_chicken_effectivenessI 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 |