R version: 4.5.1
Course: MSc: Fisheries Technology and Stock Assessment (Size Selectivity)
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyr)
We will work with selectivity curves scaled between 0 and
1.
For some dome-shaped curves (normal, double-normal), we scale so the
maximum equals 1, which makes it easier to compare
shapes across gears.
# 1.1 Logistic selectivity
# Typical for trawl codend, increasing to an asymptote at 1
# Parameters:
# L50 = length at 50% retention
# SR = selection range = L75 - L25
logistic_sel <- function(L, L50, SR) {
# For the logistic curve:
# SR = 2*log(3) / b => b = 2*log(3) / SR
b <- 2 * log(3) / SR
a <- -b * L50
1 / (1 + exp(-(a + b * L)))
}
# 1.2 Normal dome-shaped selectivity
# Typical for gillnets/hooks: catches peak at an optimal size, then drop
# Parameters:
# mu = mode (peak length)
# sigma = spread (standard deviation)
normal_sel <- function(L, mu, sigma) {
s <- exp(-0.5 * ((L - mu) / sigma)^2)
s / max(s) # scale so max = 1 for easier comparison
}
# 1.3 Double-normal (bi-normal) dome-shaped selectivity
# More flexible dome-shaped form (can be skewed, bimodal, etc.)
# Parameters:
# mu1, sigma1 = first mode - first component (main peak)
# mu2, sigma2 = second mode - second component (secondary/shoulder)
# w = weight of first mode (0–1)
double_normal_sel <- function(L, mu1, sigma1, mu2, sigma2, w = 0.5) {
s1 <- exp(-0.5 * ((L - mu1) / sigma1)^2)
s2 <- exp(-0.5 * ((L - mu2) / sigma2)^2)
s <- w * s1 + (1 - w) * s2
s / max(s) # scale so max = 1 for easier comparison
}
# Length grid used for all plots
L_grid <- seq(20, 180, by = 1)
Interpretation: gears that shift selectivity to larger fish (e.g., larger mesh) will have higher L50.
codends <- data.frame(
gear = c("Codend A", "Codend B", "Codend C"),
L50 = c(40, 55, 70), # increases
SR = c(8, 8, 8) # same SR
)
# Generate predicted selectivity for every combination of:
# - each codend (with its L50 and SR)
# - each length in L_grid
logistic_df <- codends |>
crossing(length = L_grid) |>
mutate(selectivity = logistic_sel(length, L50, SR))
ggplot(logistic_df, aes(x = length, y = selectivity, colour = gear)) +
geom_line(linewidth = 1.1) +
labs(
x = "Length (cm)",
y = "Retention probability",
colour = "Codend",
title = "Logistic selectivity – different L50, same SR"
) +
theme_minimal(base_size = 13)
codends_SR <- data.frame(
gear = c("Narrow SR", "Medium SR", "Wide SR"),
L50 = c(90, 90, 90), # same L50
SR = c(10, 25, 40) # different SR
)
logistic_SR_df <- codends_SR |>
crossing(length = L_grid) |>
mutate(selectivity = logistic_sel(length, L50, SR))
ggplot(logistic_SR_df, aes(x = length, y = selectivity, colour = gear)) +
geom_line(linewidth = 1.1) +
labs(
x = "Length (cm)",
y = "Retention probability",
colour = "Codend",
title = "Logistic selectivity – same L50, different SR"
) +
theme_minimal(base_size = 13)
Think of mesh size (or hook size) as controlling the “target-optimum”
fish size.
A simplified assumption:
\[ \mu \approx k \times \text{mesh} \]
where k is a scaling factor.
mesh <- c(5, 7.5, 10, 12.5, 15) # example mesh/hook sizes
k_mu <- 6 # scaling factor from mesh to peak length
sigmaN <- 10 # common spread for all curves
normal_df <- data.frame(mesh = mesh) |>
crossing(length = L_grid) |>
mutate(
mu = k_mu * mesh,
selectivity = normal_sel(length, mu, sigmaN)
)
ggplot(normal_df, aes(x = length, y = selectivity, colour = factor(mesh))) +
geom_line(linewidth = 1.1) +
labs(
x = "Length (cm)",
y = "Relative selectivity (scaled to 1)",
colour = "Mesh size",
title = "Normal dome-shaped selectivity – multi-mesh gears"
) +
theme_minimal(base_size = 13)
Here we allow a more flexible shape with a #secondary peak” at larger
sizes.
This can mimic skewness, shoulder, or (sometimes) bi-modality.
We first define a parameter table (one row per mesh), then expand over lengths and compute selectivity.
mesh_par <- data.frame(
mesh = mesh,
mu1 = k_mu * mesh, # main peak
mu2 = k_mu * mesh + 30, # secondary peak (shoulder) at larger sizes
sigma1 = 10,
sigma2 = 20,
w = 0.7 # 70% weight on first mode
)
double_df <- mesh_par |>
crossing(length = L_grid) |>
mutate(selectivity = double_normal_sel(length, mu1, sigma1, mu2, sigma2, w))
ggplot(double_df, aes(x = length, y = selectivity, colour = factor(mesh))) +
geom_line(linewidth = 1.1) +
labs(
x = "Length (cm)",
y = "Relative selectivity (scaled to 1)",
colour = "Mesh size",
title = "Double-normal dome-shaped selectivity – flexible curves"
) +
theme_minimal(base_size = 13)
These are tasks (you can do them during class or as homework):
L50 = c(60, 80, 100, 120)k_mu or sigmaN and interpret the effects
k_mu <- 10, sigmaN <- 20w to 0.3, 0.5, 0.9
and interpretcat("\nWELL DONE! You know understand what size selectivity means\n")
##
## WELL DONE! You know understand what size selectivity means