Supplementary Material to Gourguet et al. - Participatory qualitative modelling to assess coastal socio-ecological system sustainability

Content of this supplementary material

This Rmarkdown document provides all the supplementary material and the R code behind the results presented in the paper “Participatory qualitative modelling to assess coastal socio-ecological system sustainability …”.

The different sections detail how the results were produced from original graphs obtained during 7 workshops and from an 8th graph that proposes a Synthetic representation of the socio-ecosystem based on other models. All qualitative modelling analyses were performed using the QPress package (Melbourne-Thomas et al., 2012) according to the implementation proposed in Marzloff et al. (2016).

  • Melbourne-Thomas, J., Wotherspoon, S., Raymond, B. & Constable, A. Comprehensive evaluation of model uncertainty in qualitative network analyses. Ecol. Monogr. 82, 505–519 (2012).
  • Marzloff, M. P. et al. Modelling marine community responses to climate-driven species redistribution to guide monitoring and adaptive ecosystem-based management. Glob. Chang. Biol. 22, 2462–2474 (2016).

Import Packages

## IMPORT PACKAGES ##
library(lattice)
library(grid)
library(XML)
library(igraph)
library(gridExtra)
library(ggplot2)
library(plyr)
library(dplyr)
library(vegan)
library(ggplot2)
library(reshape2)

## Import Core QPress functions
Graph_dir <- "/Users/mpmarzlo/Documents/Collaborations/201703_projetREMAIC/QualModels_final/"
Graph_dir <- "/Users/mpmarzlo/Documents/Collaborations/201703_projetREMAIC/QualModels_Frontiers/"
source(file.path(Graph_dir, 'PlotFunctions.R'), echo = FALSE)

Import Graphs

graph_list <- list.files(Graph_dir, pattern = '2021.dia' )
setwd(Graph_dir)
invisible(list.files(pattern = '2021.dia'))

B1 = model.dia(("B1_english2021.dia")) 
B2 = model.dia(("B2_english2021.dia")) 
B3 = model.dia(("B3_english2021.dia")) 
N1 = model.dia(("N1_english2021.dia")) 
N2 = model.dia(("N2_english2021.dia")) 
N3 = model.dia(("N3_english2021.dia")) 
Re = model.dia(("Researcher_english2021.dia")) 
Sy = model.dia(("Synthetic_english2021.dia")) 

models_label = list(labelsB1 = levels(B1$From), labelsB2 = levels(B2$From), labelsB3 =levels(B3$From), labelsN1 = levels(N1$From),labelsN2 = levels(N2$From),labelsN3 = levels(N3$From), labelsSy = levels(Sy$From), labelsRe = levels(Re$From))
mod_list <- list('B1' = B1, 'B2' =B2, 'B3' = B3, 'N1'=N1, 'N2'=N2, 'N3'=N3, 'Sy'= Sy,'Re'=Re)

## PLOT MODELS FOR VISUAL CHECK
plot_graph = invisible(lapply(1:length(mod_list), function(x){
    g_B1 <- graph_from_data_frame(mod_list[[x]], directed = F)
    plot(g_B1,
    main = paste('model',names(mod_list[x])) )
} ))

## ADD DENSITY DEPENDENCE TO EACH MODEL NODE
B1 <- enforce.limitation(B1)
B2 <- enforce.limitation(B2)
B3 <- enforce.limitation(B3)
N1 <- enforce.limitation(N1)
N2 <- enforce.limitation(N2)
N3 <- enforce.limitation(N3)
Re <- enforce.limitation(Re)
Sy <- enforce.limitation(Sy)

mod_list <- list('B1' = B1, 'B2' =B2, 'B3' = B3, 'N1'=N1, 'N2'=N2, 'N3'=N3, 'Sy'= Sy,'Re'=Re)

Scenarios

Here we define 6 alternative long-term perturbations scenarios

  1. Increase in “Lease_area”
  2. Decrease in “Farming_and_land_uses”
  3. Increase in “Waste_collection” (alternatively captured as decrease in “Undersized_organic_waste” in certain models)
  4. Increase in “Shellfish_aqua_management”
  5. Increase in “Integrated_management”
  6. Decrease in “Rearing_density”
scenariosList = list(
     list("Increase in Lease Area" = c("Lease_area" = +1)),
     list("Reduction in Farming and land uses" = c("Farming_and_land_uses" = -1)),
     list("Increase in Waste collection" = c("Waste_collection" = +1)),
     list("Improved shellfish farming management" = c("Shellfish_aqua_management" = +1)),
     list("Improved integrated management" = c("Integrated_management" = +1)),
     list("Decrease in Stock density" = c("Rearing_density" = -1))    
     )

scenarios = list(
     "Lease_area" = c("Lease_area" = +1),
     "Farming_and_land_uses" = c("Farming_and_land_uses" = -1),
     "Waste_collection" = c("Waste_collection" = +1),
     "Shellfish_aqua_management" = c("Shellfish_aqua_management" = +1),
     "Integrated_management" = c("Integrated_management" = +1),
     "Rearing_density" = c("Rearing_density" = -1)) 

# Model subset per scenarios
scen_mod = do.call('rbind', lapply(models_label, function(lab){
  names(scenarios) %in% lab 
}) )
row.names(scen_mod) = names(mod_list)
colnames(scen_mod) = names(scenarios)

Output Variables

Variables highlighted in bold below summarises the responses across the 3 main sustainability components of the socio-ecosystem, i.e. ecological, economic and social.

  • Ecological components
    • Plankton
    • Nutrients
    • Pollutants and bacteria
    • Undersized organic waste
  • Economic components
    • Production
    • Stock density
    • Quality
    • Economic return
  • Social components
    • Lease Area
    • Patrimonial value
    • Social acceptability
output_var <-as.factor(c("Production", "Economic_return", "Pollutants_bacteria", "Social_acceptability", "Undersized_organic_waste", "Plankton", "Recreationnal_fishing", "Commercial_fishing"))

response_var <- as.factor(c("Plankton", "Nutrients", "Pollutants_bacteria","Undersized_organic_waste", "Production", "Rearing_density","Shellfish_quality","Economic_return","Lease_area","Social_acceptability" ))#,"Patrimonial_values"

response_3var <- as.factor(c("Undersized_organic_waste", "Economic_return","Social_acceptability" ))

Explore model structure discrepancies, and produce summary statistics

include_synth <- F
include_research <- F
nb_list_var <- F

## AGGREGATE ALL GRAPH OBJECTS FOR MODEL STRUCTURE COMPARISON

for(i in 1:length(mod_list)){
  mod_list[[i]]$Model <- as.factor(names(mod_list)[i])
}
Agg_Model <- do.call('rbind', mod_list)
#summary(Agg_Model)

# Remove synthetic and/or scientists model for plot
if(! include_synth){
Agg_Model <- Agg_Model[Agg_Model$Model!='Sy',]}
if(! include_research){
Agg_Model <- Agg_Model[Agg_Model$Model!='Re',]}
Agg_Model$Model <- factor(Agg_Model$Model) 
#summary(Agg_Model)
nb_model = nlevels(Agg_Model$Model)
nb_model
## [1] 6
# Barplot of Frequency of Variables
var_count  = as.data.frame(xtabs(~From, data = Agg_Model[Agg_Model[,'From']==Agg_Model[,'To'], ]))
var_count$Freq = as.numeric(var_count$Freq)
var_count = var_count[order(var_count$Freq, decreasing = T),]
var_count$From = factor(var_count$From, levels = as.character(var_count$From))
var_count$Var_Nb = seq(1, nrow(var_count))
var_count$Name =  as.factor(c(do.call('rbind', lapply(strsplit(as.character(var_count$From),  split='_'),function(x){paste(x, collapse = ' ')}))))
var_count$Name = factor(var_count$Name, levels = as.character(var_count$Name))

var_count$category = c('environmental')
# GREEN for following variables: Nutrients, Plankton, Pollutants_bacteria, Predation, Production, Unanticipated_losses, Undersized_organic_waste, Turbidity, Wild_shellfish
green_var =  c('Nutrients', 'Plankton', 'Pollutants_bacteria', 'Predation', 'Production', 'Unanticipated_losses', 'Undersized_organic_waste', 'Turbidity', 'Wild_shellfish')
var_count[var_count$From %in% green_var,'category'] = 'environmental'
# BLUE for following variables: Economic_return, Stock_density, Shellfish_quality, Area_concessions, Certification, Commercial_fishing, Market_price, Recreationnal_fishing, Running_costs
blue_var = c('Economic_return', 'Rearing_density', 'Shellfish_quality', 'Lease_area', 'Certification',  'Market_price', 'Running_costs','General_demand','External_production')
var_count[var_count$From %in% blue_var,'category'] = 'economic'
# RED for following variables:  Farming_and_land_uses, Shellfish_aqua_management, Social_acceptability, Integrated_management, Waste_collection, Patrimonial_values
red_var = c( 'Farming_and_land_uses', 'Shellfish_aqua_management', 'Social_acceptability', 'Integrated_management', 'Waste_collection', 'Patrimonial_values','Commercial_fishing','Recreationnal_fishing','Solid_waste')
var_count[var_count$From %in% red_var,'category'] = 'social'
var_count$category = factor(var_count$category, levels = c('social','environmental','economic'))

var_count_mod  = as.data.frame(xtabs(~From+Model, data = Agg_Model[Agg_Model[,'From']==Agg_Model[,'To'], ]))
var_count_mod$From = factor(var_count_mod$From, levels = as.character(var_count$From))
var_count_mod = var_count_mod[var_count_mod$Freq!=0,]
var_synth = merge (var_count, var_count_mod, by = 'From')



# Coloured Barplot
var_plot = var_synth[var_synth$Freq.x >1,]
var_plot$Name = factor(var_plot$Name)
levels(var_plot$Name) <- paste(seq(1,nlevels(var_plot$Name)), levels(var_plot$Name))


p <- ggplot(data = var_plot, aes(x = Name, y = Model, fill = Freq.y, col= category ) )+
  theme(legend.position="none", axis.text.x=element_text(angle=45,hjust=1)) +
  geom_tile(width = .8, height = .4, size = 2)+ 
  scale_fill_gradient(low='darkgrey')+
  xlab("Variables")+ylab("Models")+
  ggtitle("Variables Inclusion In Stakeholders' Models")
p

p <- ggplot(data = var_plot, aes(x = Model, y = Name, fill = Freq.y, col= category ) )+
  theme(legend.position="none") +
  geom_tile(width = .8, height = .4,  size = 1)+ 
  scale_fill_gradient(low='darkgrey')+
  xlab("Models")+ylab("Variables")+
  ggtitle("Variables Inclusion In Stakeholders' Models")+
  scale_y_discrete(limits = rev(levels(var_plot$Name)))

p0 <- p + theme(
plot.title = element_text(color="black", size=14, face="italic"),
axis.title.x = element_text(color="black", size=14, face="bold"),
axis.title.y = element_text(color="black", size=14, face="bold"),
plot.margin=unit(c(1,1,1,1.5),"cm") )
p0

# Distance between models

dist_mod = t(xtabs(~From+Model, data = Agg_Model[Agg_Model[,'From']==Agg_Model[,'To'], ]))
rownames(dist_mod)
## [1] "B1" "B2" "B3" "N1" "N2" "N3"
dist_mod=(dist(t(xtabs(~From+Model, data = Agg_Model[Agg_Model[,'From']==Agg_Model[,'To'], ])), method = 'binary',diag = T, upper = T))
dist_mod = data.matrix(dist_mod)
dist_mod = melt(dist_mod)

ggplot(data = dist_mod, aes(x = Var1, y = Var2, fill = value )  )+  
  theme(legend.position="none", axis.text.x=element_text(angle=45,hjust=1)) +
  geom_tile(width = .8, height = .4)+ 
  scale_fill_gradient(low='white', high= 'black')

# Other Barplots about Variables Inclusion in Models

p <- ggplot( 
  data = var_count, aes(x = From, y = Freq ) ) +
  theme(axis.text.x=element_text(angle=45,hjust=1)) +
  geom_bar (stat= 'identity')+
  xlab("Variables")+ylab("Frequency")+
  ggtitle("Frequency of Variables Inclusion Across All Models")

p1 <- p + theme(
plot.title = element_text(color="red", size=14, face="italic"),
axis.title.x = element_text(color="blue", size=14, face="bold"),
axis.title.y = element_text(color="#993333", size=14, face="bold"),
plot.margin=unit(c(1,1,1,1.5),"cm") )

p <- ggplot( 
  data = var_count[var_count$Freq>1,], aes(x = From, y = Freq ) ) +
  theme(axis.text.x=element_text(angle=45,hjust=1)) +
  geom_bar (stat= 'identity')+
  xlab("Variables")+ylab("Frequency")+
  ggtitle("Frequency of Variables Inclusion Across All Models")

p2 <- p + theme(
plot.title = element_text(color="red", size=14, face="italic"),
axis.title.x = element_text(color="blue", size=14, face="bold"),
axis.title.y = element_text(color="#993333", size=14, face="bold"),
plot.margin=unit(c(1,1,1,1.5),"cm") )

gridExtra::grid.arrange(p1,p2, nrow = 2)

Explore model structure discrepancies via graph representations

include_research <- F
nb_list_var <- F

## AGGREGATE ALL GRAPH OBJECTS FOR MODEL STRUCTURE COMPARISON

for(i in 1:length(mod_list)){
  mod_list[[i]]$Model <- as.factor(names(mod_list)[i])
}
Agg_Model <- do.call('rbind', mod_list)
#summary(Agg_Model)

# Remove scientists model for plot
if(! include_research){
Agg_Model <- Agg_Model[Agg_Model$Model!='Re',]}
Agg_Model$Model <- factor(Agg_Model$Model) 
Agg_Model$From <- factor(Agg_Model$From)
Agg_Model$To <- factor(Agg_Model$To)
#summary(Agg_Model)
nb_model = nlevels(Agg_Model$Model)
nb_model
## [1] 7
# Change Variable Names to Numbers For Graphs
Agg_Model$NameFrom <- Agg_Model$From 
Agg_Model$NameTo <- Agg_Model$To
levels(Agg_Model$From) <- as.character(seq(1,nlevels(Agg_Model$From)))
levels(Agg_Model$To  ) <- as.character(seq(1,nlevels(Agg_Model$To)))

# Remove self limitation 
Agg_ModPlot <- Agg_Model[Agg_Model[,'From']!=Agg_Model[,'To'], ]

# Put Model Synthetic First in the list
Agg_ModPlot$Model <- factor(Agg_ModPlot$Model, levels = levels(Agg_ModPlot$Model)[c(7,1:6)] ) 
nb_model = nlevels(Agg_ModPlot$Model)


# Summarise information per model
nodes = as.data.frame( xtabs( ~ From, data= Agg_ModPlot ))
names(nodes)[2]  = "Nb_From"
nodes = cbind(nodes, list('Nb_To' = c(xtabs( ~ To, data= Agg_ModPlot ))) ) 
nodes = cbind(nodes, list('Model' = rowSums(xtabs( ~ From+Model, data= Agg_ModPlot )))) 
nodes = cbind(nodes, matrix(xtabs( ~ From+Model, data= Agg_ModPlot ), ncol= nb_model) ) 
nodes = cbind(nodes, matrix(xtabs( ~  To + Model, data= Agg_ModPlot), ncol= nb_model))   
names(nodes)[5:ncol(nodes)] <- c(paste0("From", 1:nb_model), paste0("To", 1:nb_model))

nodes$Model1 = nodes$From1 > 0 | nodes$To1 > 0
nodes$Model2 = nodes$From2 > 0 | nodes$To2 > 0
nodes$Model3 = nodes$From3 > 0 | nodes$To3 > 0
nodes$Model4 = nodes$From4 > 0 | nodes$To4 > 0
nodes$Model5 = nodes$From5 > 0 | nodes$To5 > 0
nodes$Model6 = nodes$From6 > 0 | nodes$To6 > 0
nodes$Model7 = nodes$From7 > 0 | nodes$To7 > 0
ind_start = 19
if(include_synth){
nodes$Model8 = nodes$From8 > 0 | nodes$To8 > 0
ind_start = 21}

nodes$ModelInclude = apply(nodes[,seq(ind_start,(ind_start+nb_model-1))], 1, FUN = function(x) paste(as.character(1:nb_model)[x], collapse= '_') )
nodes$Nb_Include = rowSums(nodes[,ind_start:(ind_start+nb_model-1)])
nodes$Model_graph = as.numeric(substr(nodes$ModelInclude, start=1, stop=1))
nodes$Model_graph[nodes$Nb_Include == 2] = 8

# RE-order variables in ascending order
row.names(nodes) = levels(Agg_ModPlot$NameFrom)
nodes = nodes[as.character(var_count$From[var_count$Freq >0]),]
nodes$From = 1:nrow(nodes)
#summary(nodes)
 
# Remove Duplicated Edges and Count Number of Occurrence
interactionCount = as.data.frame(xtabs( ~ From+To+Type, data= Agg_ModPlot ) ) 
Agg_ModPlot = Agg_ModPlot[! duplicated(Agg_ModPlot[,c('From','To','Type')]), ]
Agg_ModPlot = merge(Agg_ModPlot, interactionCount)
Agg_ModPlot$ModelInclus = factor(Agg_ModPlot$Model, levels= c(levels(Agg_ModPlot$Model),"Pair"))
Agg_ModPlot$ModelInclus[Agg_ModPlot$Freq==2] = "Pair"
Agg_ModPlot$ModelNb = Agg_ModPlot$ModelInclus
levels(Agg_ModPlot$ModelNb) = 1:nlevels(Agg_ModPlot$ModelNb)
#summary(Agg_ModPlot)

# Generate colors based on model
dir_graph= F
g_Agg = graph_from_data_frame(d= Agg_ModPlot, vertices= nodes, directed = dir_graph)

if(include_synth){
colrs = c("gray50", "tomato", "gold","lightblue","darkgreen","blanchedalmond","lawngreen","dodgerblue3","lightgrey")
}else{
  colrs = c("gray50", "tomato", "gold","lightblue","blanchedalmond","lawngreen","dodgerblue3","lightgrey")
}
# Set Node Colour based on Model
V(g_Agg)$color <- colrs[V(g_Agg)$Model_graph]
# Set node size based on Nb Of Inclusion
V(g_Agg)$size <- (V(g_Agg)$Nb_Include+10)*1.2
# Set Node Label colour
V(g_Agg)$label.color <- 'black'

# Set edge width based on weight:
if(dir_graph){
E(g_Agg)$width <- E(g_Agg)$Freq/2
E(g_Agg)$arrow.size <- .2
E(g_Agg)$edge.colour <- colrs[E(g_Agg)$ModelNb]
}else{
E(g_Agg)$width <- 1  
}


# Plot graphs

# Define legends positions
x_leg =  c(-1.7,1,1.4)
y_leg =  c(1.5, 1.3)  
cex_leg = c(1.2, .5)
nb_list_var = F

set.seed(29)
plot(g_Agg,
    vertex.label.cex = 0.8, 
  edge.width = E(g_Agg)$weight, 
    edge.arrow.size= .2,
    edge.curved=.1,
  layout=layout_with_lgl(graph=g_Agg, cellsize = 10e+7),
    main = paste('Comparing Model Structures (LGL projections)' ))
levels(Agg_Model$Model)
## [1] "B1" "B2" "B3" "N1" "N2" "N3" "Sy"
legend(x=x_leg[1], y= y_leg[1], levels(Agg_ModPlot$ModelInclus), pch=21,
       col="#777777", pt.bg=colrs, pt.cex=2, cex= cex_leg[1], bty="n", ncol=1, title = 'Model')

if(nb_list_var){
legend(x=x_leg[2], y= y_leg[2], paste(1:35,rownames(nodes)[1:35],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
legend(x=x_leg[3], y= y_leg[2], paste(36:nrow(nodes),rownames(nodes)[36:nlevels(Agg_ModPlot$NameFrom)],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
}

plot(g_Agg,
vertex.label.cex = 0.8, 
  edge.width = E(g_Agg)$weight, 
    edge.arrow.size= .2,
    edge.curved=.1,
  layout=layout_with_graphopt,
    main = paste('Comparing Model Structures (Graph-opt projection)' ))
legend(x=x_leg[1], y= y_leg[1], levels(Agg_ModPlot$ModelInclus), pch=21,
       col="#777777", pt.bg=colrs, pt.cex=4, cex= cex_leg[1], bty="n", ncol=1, title = 'Model')

if(nb_list_var){
legend(x=x_leg[2], y= y_leg[2], paste(1:35,rownames(nodes)[1:35],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
legend(x=x_leg[3], y= y_leg[2], paste(36:nrow(nodes),rownames(nodes)[36:nlevels(Agg_ModPlot$NameFrom)],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
}

plot(g_Agg,
    vertex.label.cex = 0.8, 
  edge.width = E(g_Agg)$weight, 
    edge.arrow.size= .2,
    edge.curved=.1,
  layout=layout_nicely,
    main = paste('Comparing Model Structures (aesthetic layout)' ))
legend(x=x_leg[1], y= y_leg[1], levels(Agg_ModPlot$ModelInclus), pch=21,
       col="#777777", pt.bg=colrs, pt.cex=4, cex= cex_leg[1], bty="n", ncol=1, title = 'Model')

if(nb_list_var){
legend(x=x_leg[2], y= y_leg[2], paste(1:35,rownames(nodes)[1:35],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
legend(x=x_leg[3], y= y_leg[2], paste(36:nrow(nodes),rownames(nodes)[36:nlevels(Agg_ModPlot$NameFrom)],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
}

plot(g_Agg,
    vertex.label.cex = 0.8, 
  edge.width = E(g_Agg)$weight, 
    edge.arrow.size= .2,
    edge.curved=.1,
  layout=layout_on_sphere,
    main = paste('Comparing Model Structures (sphere layout)' ))
legend(x=x_leg[1], y= y_leg[1], levels(Agg_ModPlot$ModelInclus), pch=21,
       col="#777777", pt.bg=colrs, pt.cex=4, cex= cex_leg[1], bty="n", ncol=1, title = 'Model')

if(nb_list_var){
legend(x=x_leg[2], y= y_leg[2], paste(1:35,rownames(nodes)[1:35],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
legend(x=x_leg[3], y= y_leg[2], paste(36:nrow(nodes),rownames(nodes)[36:nlevels(Agg_ModPlot$NameFrom)],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.5, cex= cex_leg[2], bty="n", ncol=1)
}


print( paste(1:nlevels(Agg_ModPlot$NameFrom), levels(Agg_ModPlot$NameFrom)) )
##  [1] "1 Catchment_runoff"               "2 Certification"                 
##  [3] "3 Economic_return"                "4 Farming_and_land_uses"         
##  [5] "5 Favourable_weather_regime"      "6 General_demand"                
##  [7] "7 Nutrients"                      "8 Plankton"                      
##  [9] "9 Pollutants_bacteria"            "10 Predation"                    
## [11] "11 Production"                    "12 Rearing_density"              
## [13] "13 Shellfish_aqua_management"     "14 Social_acceptability"         
## [15] "15 Solid_waste"                   "16 Turbidity"                    
## [17] "17 Unanticipated_losses"          "18 Undersized_organic_waste"     
## [19] "19 Wild_shellfish"                "20 Commercial_fishing"           
## [21] "21 External_production"           "22 Infrastructure_processing"    
## [23] "23 Integrated_management"         "24 Market_price"                 
## [25] "25 Patrimonial_values"            "26 Recreationnal_fishing"        
## [27] "27 Shellfish_quality"             "28 Waste_collection"             
## [29] "29 Aquaculture_heritage"          "30 By_product"                   
## [31] "31 Lease_area"                    "32 Local_economy"                
## [33] "33 Tourism"                       "34 Demand_low_quality"           
## [35] "35 High_quality_price"            "36 Low_quality_price"            
## [37] "37 Other_uses"                    "38 Running_costs"                
## [39] "39 Site_suitability"              "40 Beach_users_conflicts"        
## [41] "41 Boating"                       "42 Response_to_mortality"        
## [43] "43 Stealing"                      "44 Benthos"                      
## [45] "45 BOD"                           "46 Dams"                         
## [47] "47 DOM"                           "48 Floods"                       
## [49] "49 Light"                         "50 POM"                          
## [51] "51 Primary_production_monitoring"
plot.new()
if(!nb_list_var){
legend(x=-0.1, y= .9, paste(1:35,rownames(nodes)[1:35],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.9, cex= cex_leg[2], bty="n", ncol=1)
legend(x=.5, y= .9,  paste(36:nrow(nodes),rownames(nodes)[36:nlevels(Agg_ModPlot$NameFrom)],sep =' - '), pch=NA,
       col="#777777", pt.bg=colrs, pt.cex=.9, cex= cex_leg[2], bty="n", ncol=1)
}


Simulate Long-Term Effects of Alternative Scenarios

Colour scaling in the plots is as follows: - black corresponds to the absence of the response node in a given model; - white corresponds to a null response (no effect of the scenario on response node, which can occur when model does not include input node); - a continuous blue-grey-red colour bar is used to represent negative, ambiguous and positive responses, respectively.

# Generate plots
plot_matrix = lapply(1:length(scenarios), function(i){
selecMod = scen_mod[,names(scenarios[[i]])]

plotResults(mod_list = mod_list[selecMod], 
            scenario_list = scenariosList[i],
            label_list=  models_label[selecMod],
            output_var = response_var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = names(scenariosList[[i]])) 
})


# Display results

plot_matrix[[1]]

plot_matrix[[2]]

plot_matrix[[3]]

plot_matrix[[4]]

plot_matrix[[5]]

plot_matrix[[6]]


Focus on Synthetic Model Predictions Only

selecMod = 7

plotResults_singModel(model = mod_list[selecMod][[1]], 
            scenario_list = scenariosList,
            label_list=  models_label[selecMod],
            output_var = response_var,
            label_x = c("Lease Area +", "Farming and land uses -", "Waste collection +","Shellfish aqua. mgt. +", "Integrated mgt. +","Stock density -" ), #names(scenarios),
            main_title = 'Synthetic Model Predictions') 


Focus on Area of Concession Scenario

  1. Increase in “Lease_area”
invisible(scenarios)
scen_mod_noN3 = scen_mod
scen_mod_noN3['N3',] = FALSE
selecMod = scen_mod_noN3[,names(scenarios[[1]])]

plotResults(mod_list =  mod_list[selecMod],
            scenario_list = scenariosList[1],
            label_list= models_label[selecMod],
            output_var = response_3var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = 'Lease Area Increase') 

plotResults(mod_list =  mod_list,
            scenario_list = scenariosList[1],
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Lease Area Increase') 
## Warning in index(names(perturb)): Unknown nodes:Lease_area

## Warning in index(names(perturb)): Unknown nodes:Lease_area

## Warning in index(names(perturb)): Unknown nodes:Lease_area


Focus on Farming and Land Uses Scenario

  1. Decrease in “Farming_and_land_uses”
selecMod = scen_mod_noN3[,names(scenarios[[2]])]

plotResults(mod_list =  mod_list[selecMod],
            scenario_list = scenariosList[2],
            label_list= models_label[selecMod],
            output_var = response_3var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = 'Farming and Land Uses Reduction') 

plotResults(mod_list =  mod_list,
            scenario_list = scenariosList[2],
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Farming and Land Uses Reduction') 
## Warning in index(names(perturb)): Unknown nodes:Farming_and_land_uses

## Warning in index(names(perturb)): Unknown nodes:Farming_and_land_uses


Focus on Waste Reduction Scenario

  1. Increase in “Waste_collection” or: Decrease in “Undersized_organic_waste”
scenario_waste = list( list('B1' = c("Undersized_organic_waste"=-1)),
                            list('B2' = c("Waste_collection"= +1)),
                            list('B3' = c("Waste_collection"= +1)),
                           list('N2' = c("Waste_collection"= +1)),
                           list('Sy' = c("Waste_collection"= +1)),
                           list('Re' = c("Undersized_organic_waste"= -1)))
models_waste = list('B1' = B1, 'B2'= B2, 'B3'=B3,'N2'=N2,'Sy'=Sy,'Re'=Re)

plotResults(mod_list = models_waste, scenario_list = scenario_waste,
            label_list= models_label[c(1,2,3,5,7,8)],
            output_var = response_3var,
            label_x = names(models_waste),
            Scenario_name = 'Waste Reduction') 

scenario_waste_all = list( list('B1' = c("Undersized_organic_waste"=-1)),
                            list('B2' = c("Waste_collection"= +1)),
                            list('B3' = c("Waste_collection"= +1)),
                           list('N1' = c("Waste_collection"= +1)),
                           list('N2' = c("Waste_collection"= +1)),
                           list('N3' = c("Waste_collection"= +1)),
                           list('Sy' = c("Waste_collection"= +1)),
                           list('Re' = c("Undersized_organic_waste"= -1)))

plotResults(mod_list = mod_list,
            scenario_list = scenario_waste_all,
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Waste Reduction') 
## Warning in index(names(perturb)): Unknown nodes:Waste_collection

## Warning in index(names(perturb)): Unknown nodes:Waste_collection


Focus on Shellfish aquaculture management Scenario

  1. Increase in “Shellfish_aqua_management”
selecMod = scen_mod_noN3[,names(scenarios[[4]])]

plotResults(mod_list =  mod_list[selecMod],
            scenario_list = scenariosList[4],
            label_list= models_label[selecMod],
            output_var = response_3var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = 'Shellfish aquaculture management') 

plotResults(mod_list =  mod_list,
            scenario_list = scenariosList[4],
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Shellfish aquaculture management') 
## Warning in index(names(perturb)): Unknown nodes:Shellfish_aqua_management


Focus on Waste Reduction Scenario

  1. Increase in “Integrated_management”
selecMod = scen_mod_noN3[,names(scenarios[[5]])]

plotResults(mod_list =  mod_list[selecMod],
            scenario_list = scenariosList[5],
            label_list= models_label[selecMod],
            output_var = response_3var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = 'Integrated management') 

plotResults(mod_list =  mod_list,
            scenario_list = scenariosList[5],
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Integrated management') 
## Warning in index(names(perturb)): Unknown nodes:Integrated_management

## Warning in index(names(perturb)): Unknown nodes:Integrated_management

## Warning in index(names(perturb)): Unknown nodes:Integrated_management


Focus on Stock Density Reduction Scenario

  1. Decrease in “Rearing_density”
selecMod = scen_mod_noN3[,names(scenarios[[6]])]

plotResults(mod_list =  mod_list[selecMod],
            scenario_list = scenariosList[6],
            label_list= models_label[selecMod],
            output_var = response_3var,
            label_x = names(mod_list[selecMod]),
            Scenario_name = 'Rearing Density Reduction') 

plotResults(mod_list =  mod_list,
            scenario_list = scenariosList[6],
            label_list= models_label,
            output_var = response_3var,
            label_x = names(mod_list),
            Scenario_name = 'Rearing Density Reduction') 
## Warning in index(names(perturb)): Unknown nodes:Rearing_density

## Warning in index(names(perturb)): Unknown nodes:Rearing_density


Sensitivity of model results to scenarios and model structures

CAUTION - Performing multivariate analyses on numerical values associated with qualitative modelling requires attention to non-intuitive meaning of numeric outputs (NA, negative and positive or missing value). This is not as simple as treating the matrices as meaningful numeric outcomes of a scenario.

Generate all results

mat_ScenMod = lapply(1:length(scenarios), function(i){

  selecMod = scen_mod[,names(scenarios[[i]])]

  result = as.matrix(generateResults_singScen(mod_list = mod_list[selecMod], 
            scenario_list = scenariosList[i],
            label_list=  models_label[selecMod],
             output_var = response_var) )

    # FILTER OUT LARGE VALUES (I.E. NO RESPONSE)
    result[result>1] = 1+1/60
    colnames(result) = paste(names(mod_list[selecMod]),names(scenarios[[i]]), sep = '_')
  result
})

mat_ScenMod <- do.call('cbind',mat_ScenMod)
save(mat_ScenMod, file = 'SuppMat_matrixScenarioModels.Rdata')

#print(mat_ScenMod)

print("Values are in [-1, 1] when input and output variables are connnected, =NA when input variables does not impact output variables (which displays in white), = 1+1/60 i.e. 1.016667 when either input or output variables are absent from the model (which displays in black).")
## [1] "Values are in [-1, 1] when input and output variables are connnected, =NA when input variables does not impact output variables (which displays in white), = 1+1/60 i.e. 1.016667 when either input or output variables are absent from the model (which displays in black)."

Multivariate Analysis of Scenario Results

Numerical values are not directly useable in a multivariate analysis so need to be modified.

load(file = 'SuppMat_matrixScenarioModels.Rdata')
#summary(mat_ScenMod)

# PCA To Inspect variability across simulations


pca_matrix <- data.frame(t(mat_ScenMod))
pca_matrix$model = as.factor(substr(rownames(pca_matrix), start = 1, stop = 2) )
pca_matrix$scenario = as.factor(substr(rownames(pca_matrix), start = 4, stop = 40))
#summary(pca_matrix)

# Deal with gaps in matrix (i.e. 1+1/60) by replacing them by mean values for each variable under each scenario
gap_value = 1+1/60 
pca_matrix[is.na(pca_matrix)] = 0
pca_matrix[(pca_matrix) == gap_value ] = NA

# Replace missing values by variables mean response for a given scenario
var_mean = aggregate(pca_matrix[,1:length(response_var)], by = list(pca_matrix$scenario), FUN = function(x){mean(x, na.rm = T)})
for(var in 1:11){
pca_matrix[is.na(pca_matrix[,var]),var] <- 
  var_mean[ pca_matrix[is.na(pca_matrix[,var]),'scenario'], var+1]
}

# Perform PCA

pca_veg <- rda(pca_matrix[,-c((ncol(pca_matrix)-1):ncol(pca_matrix))])
plot(pca_veg,type="text", scaling = 1)

plot(pca_veg,type="text", scaling = 2)

scores(pca_veg, choices = c(1,2), scaling = 2)
## $species
##                                 PC1          PC2
## Plankton                  0.8658424  0.192542950
## Nutrients                -0.2880156  0.977455447
## Pollutants_bacteria       0.3414417  0.831395687
## Undersized_organic_waste -0.9979371 -0.311495868
## Production               -1.1010014 -0.009636335
## Rearing_density          -0.2834886 -0.091683072
## Shellfish_quality         0.3919644 -0.796252428
## Economic_return           0.3622079 -0.708638803
## Lease_area               -0.5645641  0.531269474
## Social_acceptability      0.9567860  0.368103470
## 
## $sites
##                                      PC1         PC2
## B3_Lease_area                -0.86244673  0.17738792
## N1_Lease_area                -1.21937330  0.75243392
## N2_Lease_area                -0.74350034 -0.22113332
## Sy_Lease_area                -0.38491915 -0.13241315
## Re_Lease_area                -1.02037232  0.21688394
## B1_Farming_and_land_uses     -0.52840444 -0.75494347
## B2_Farming_and_land_uses     -0.86030481 -1.04294239
## N2_Farming_and_land_uses      0.52351184 -0.65092036
## N3_Farming_and_land_uses     -0.13839490 -0.67888295
## Sy_Farming_and_land_uses     -0.04393543 -0.54540070
## Re_Farming_and_land_uses     -0.03498854 -0.72959781
## B2_Waste_collection           0.33722482  1.25744122
## B3_Waste_collection          -0.47216458  0.87891124
## N2_Waste_collection          -0.46953930  0.82104866
## Sy_Waste_collection          -0.17791998  0.70242192
## B1_Shellfish_aqua_management  0.74438135  0.39522708
## B2_Shellfish_aqua_management -0.04681466 -0.12675994
## B3_Shellfish_aqua_management -0.27774873  0.10901039
## N1_Shellfish_aqua_management  0.71320129 -0.39698568
## N3_Shellfish_aqua_management  0.01043267  0.11852468
## Sy_Shellfish_aqua_management  0.23620551  0.72335679
## Re_Shellfish_aqua_management -0.71466791 -0.04771455
## B2_Integrated_management     -0.26087781 -0.80646915
## N1_Integrated_management      0.77105010 -0.36313426
## N3_Integrated_management      0.26212919 -0.47165852
## Sy_Integrated_management      0.35968759 -0.38824393
## Re_Integrated_management      0.72820917 -0.48526864
## B1_Rearing_density            0.80954914  0.13842373
## B2_Rearing_density            0.72926886  0.84395319
## N1_Rearing_density            0.71118693 -0.42344733
## N2_Rearing_density            0.57133455  0.57763589
## N3_Rearing_density            0.31365051  0.20475212
## Sy_Rearing_density            0.43534945  0.34850348
## 
## attr(,"const")
## [1] 3.359213
pca <- prcomp((pca_matrix[,-c((ncol(pca_matrix)-1):ncol(pca_matrix))]))
plot(pca$x[,1:2] )
text(pca$x[,1:2], rownames(pca_matrix) )

biplot(pca, var.axes = T)

#summary(pca)

var_expl <- pca$sd^2
var_expl <- round(var_expl / sum(var_expl)*100 , digits = 1)

## PCA per Model and per Scenario ##

# Prepare data.frame 'dfForPCA' #
dfForPCA <- data.frame(PC1 = pca$x[,1], PC2 = pca$x[,2], Model = pca_matrix$model, Scenario = pca_matrix$scenario)
levels(dfForPCA$Model)
## [1] "B1" "B2" "B3" "N1" "N2" "N3" "Re" "Sy"
levels(dfForPCA$Scenario)
## [1] "Farming_and_land_uses"     "Integrated_management"    
## [3] "Lease_area"                "Rearing_density"          
## [5] "Shellfish_aqua_management" "Waste_collection"
# Prepare projections of variables onto PC1 and PC2 #
df_vecPCA <- data.frame(x = pca$rotation[,1],
            y = pca$rotation[,2])
df_vecPCA$norm <- sqrt(df_vecPCA$x^2 + df_vecPCA$y^2)

#getting the convex hull for a given point set
find_hull <- function(dfForPCA) dfForPCA[chull(dfForPCA$PC1, dfForPCA$PC2), ]

#Adjust Scenario Names
levels(dfForPCA$Scenario)
## [1] "Farming_and_land_uses"     "Integrated_management"    
## [3] "Lease_area"                "Rearing_density"          
## [5] "Shellfish_aqua_management" "Waste_collection"
levels(dfForPCA$Scenario) <- c("Decrease in Farming and Land Uses","Increase in Integrated Management","Increase in Lease Area","Decrease in Rearing Density","Increase in Shellfish Aqua. Management","Increase in Waste Collection")

# Basic scatter plot
p <- ggplot(data = dfForPCA, aes(x= PC1, y= PC2, colour = Model, shape= Scenario)) +
  geom_point(size=6) +
  scale_shape_manual(values=1:nlevels(dfForPCA$Scenario))+
  theme(text = element_text(size=15), legend.key.size = unit(1.1,'cm'))+
  labs(x = paste('PC 1 (', var_expl[1],'% of total variance)' ),
       y = paste('PC 2 (', var_expl[2],'% of total variance)' ))
p      

hulls <- ddply(dfForPCA, "Scenario", find_hull)
p <- ggplot(data = dfForPCA, aes(x= PC1, y= PC2, colour = Scenario,fill = Scenario, shape= Model)) +
  geom_point(size=6) +
  scale_shape_manual(values=1:nlevels(dfForPCA$Model))+
  geom_polygon(data= hulls,aes( x=PC1, y=PC2,colour= Scenario, group = Scenario), alpha = .2) +
  theme(text = element_text(size=15), legend.key.size = unit(1.1,'cm'))+
  labs(x = paste('PC 1 (', var_expl[1],'% of total variance)' ),
       y = paste('PC 2 (', var_expl[2],'% of total variance)' ))
p      

hulls <- ddply(dfForPCA, "Model", find_hull)
p <- ggplot(data = dfForPCA, aes(x= PC1, y= PC2, colour = Model, fill = Model, shape = Scenario)) +
  geom_point(size=6) +
  geom_polygon(data= hulls,aes( x=PC1, y=PC2,colour= Model, group = Model), alpha = .2) +
  theme(text = element_text(size=15), legend.key.size = unit(1.1,'cm'))+
  labs(x = paste('PC 1 (', var_expl[1],'% of total variance)' ),
       y = paste('PC 2 (', var_expl[2],'% of total variance)' ))
p      


Summary per variables

# Summary across raw numerical predictions
numerical_mat <-  data.frame(t(mat_ScenMod))
numerical_mat$model = as.factor(substr(rownames(numerical_mat), start = 1, stop = 2) )
numerical_mat$scenario = as.factor(substr(rownames(numerical_mat), start = 4, stop = 40))
#summary(numerical_mat)

# Deal with gaps in matrix (i.e. 1+1/60) by replacing them by mean values for each variable under each scenario
gap_value = 1+1/60 
numerical_mat[is.na(numerical_mat)] = 0
numerical_mat[(numerical_mat) == gap_value ] = NA

apply(numerical_mat,2, function(x){c('Mean' = mean(x, na.rm = T), 'SD' = sd(x, na.rm = T), 'Proportion of occurence' = sum(!is.na(x))/length(x) )} )
## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA

## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA
## Warning in var(if (is.vector(x) || is.factor(x)) x else as.double(x), na.rm =
## na.rm): NAs introduced by coercion
## Warning in mean.default(x, na.rm = T): argument is not numeric or logical:
## returning NA
## Warning in var(if (is.vector(x) || is.factor(x)) x else as.double(x), na.rm =
## na.rm): NAs introduced by coercion
##                          Plankton Nutrients Pollutants_bacteria
## Mean                           NA        NA                  NA
## SD                      0.6707240 0.6789458           0.6586542
## Proportion of occurence 0.9090909 0.6666667           0.6666667
##                         Undersized_organic_waste Production Rearing_density
## Mean                                          NA         NA              NA
## SD                                     0.7783254  0.7093752       0.4385780
## Proportion of occurence                0.7575758  0.9090909       0.7878788
##                         Shellfish_quality Economic_return Lease_area
## Mean                                   NA              NA         NA
## SD                              0.6862848       0.6313439  0.7117441
## Proportion of occurence         0.5757576       0.7878788  0.6363636
##                         Social_acceptability model scenario
## Mean                                      NA    NA       NA
## SD                                 0.7815227    NA       NA
## Proportion of occurence            0.7575758     1        1
# Summary across qualitative sign predictions (treated as 'positive / negative / neutral / ambiguous')
model_list2 = as.factor(substr(colnames(mat_ScenMod), start = 1, stop = 2) )
scenario_list2 = as.factor(substr(colnames(mat_ScenMod), start = 4, stop = 40))

mat_RespType = as.data.frame(mat_ScenMod)
mat_RespType[is.na(mat_ScenMod)] = 'Neutral'
mat_RespType[mat_ScenMod> .33]  = 'Positive'
mat_RespType[mat_ScenMod< -.33]  = 'Negative'
mat_RespType[mat_ScenMod>= -.33 & mat_ScenMod<= .33]  = 'Ambiguous'
mat_RespType[mat_ScenMod> 1]  = NA

varSensi <- as.data.frame(t(mat_RespType))
varSensi$Model <- as.factor(substr(colnames(mat_ScenMod), start = 1, stop = 2) )
varSensi$Scenario <- as.factor(substr(colnames(mat_ScenMod), start = 4, stop = 40))
#summary(varSensi)

# Compute Simpson index (= 1 if all qualitative predictions are consistent) and proportion of ambiguous predictions for each variable under each scenario across all models. Then compute the mean of both these indices for each variable.

d <- sapply(response_var, function(i){ 
  temp <- as.data.frame(with(varSensi, table(varSensi[,as.character(i)], Scenario)))
  var_output = sapply(levels(temp$Scenario), function(scenar){
    temp$Var1 <- factor(temp$Var1, levels = c("Negative","Neutral","Positive","Ambiguous"))
    temp2 = xtabs(Freq~Var1, data = temp[temp$Scenario == scenar,]) 
    simpson = sum((temp2/sum(temp2))^2)
    ambig = temp2["Ambiguous"]  / sum(temp2)
    (data.frame(Simpson= simpson, Ambig = ambig))
    } ) 
  var_output = matrix(apply(var_output,1,function(x) round(mean(as.numeric(x) ), digits= 2)), ncol=2)
  var_output
  })

rownames(d) = c('Simpson','Ambiguous')
colnames(d) = response_var

d <- as.data.frame(t(d))
d$Variable= response_var

p <- ggplot(data = d, aes(x= Ambiguous, y= Simpson, colour = Variable, shape= Variable)) +
  geom_point(size=10) +
  scale_shape_manual(values=1:length(response_var))+
  theme(text = element_text(size=15), legend.key.size = unit(1.1,'cm'))+
  scale_x_continuous(limits = c(0,.4), name = 'Mean proportion of ambiguous predictions' )+
  scale_y_continuous(limits = c(.2,1), name = 'Mean Simpson index' )
p      


Session info

sessionInfo()
## R version 4.1.0 (2021-05-18)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Mojave 10.14.6
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] tcltk     grid      stats     graphics  grDevices utils     datasets 
## [8] methods   base     
## 
## other attached packages:
##  [1] MASS_7.3-54        tcltk2_1.2-11      RColorBrewer_1.1-2 reshape2_1.4.4    
##  [5] vegan_2.5-7        permute_0.9-5      dplyr_1.0.6        plyr_1.8.6        
##  [9] ggplot2_3.3.3      gridExtra_2.3      igraph_1.2.6       XML_3.99-0.6      
## [13] lattice_0.20-44   
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.1.1  xfun_0.23         purrr_0.3.4       splines_4.1.0    
##  [5] colorspace_2.0-1  vctrs_0.3.8       generics_0.1.0    htmltools_0.5.1.1
##  [9] yaml_2.2.1        mgcv_1.8-36       utf8_1.2.1        rlang_0.4.11     
## [13] pillar_1.6.1      glue_1.4.2        withr_2.4.2       lifecycle_1.0.0  
## [17] stringr_1.4.0     munsell_0.5.0     gtable_0.3.0      evaluate_0.14    
## [21] labeling_0.4.2    knitr_1.33        rmdformats_1.0.2  parallel_4.1.0   
## [25] fansi_0.5.0       highr_0.9         Rcpp_1.0.6        scales_1.1.1     
## [29] farver_2.1.0      digest_0.6.27     stringi_1.6.2     bookdown_0.22    
## [33] tools_4.1.0       magrittr_2.0.1    tibble_3.1.2      cluster_2.1.2    
## [37] crayon_1.4.1      pkgconfig_2.0.3   ellipsis_0.3.2    Matrix_1.3-4     
## [41] rmarkdown_2.8     R6_2.5.0          nlme_3.1-152      compiler_4.1.0