Ce document donne un exemple de chaîne de traitement des données Aspe pour produire des relations taille - poids.
Ces relations se veulent aussi générales que possible, donc d’éventuelles observations atypiques, voire des erreurs dans la base de données, ne doivent pas les influencer exagérément. Une vérification de la crédibilité des résultats est réalisée en les confrontant aux relations taille-poids publiées et diffusées par la base en ligne Fishbase.
Ces relations peuvent être utiles pour comparer des populations entre elles ainsi que pour estimer la masse des poissons quand on connaît leur longueur, par exemple afin de calculer des densités de biomasse piscicoles en \(g\) (de poisson capturé) par \(m^2\) prospecté dans les cas - nombreux dans cette base et ailleurs - où toutes les captures ne sont pas pesées sur le terrain.
Nous supposons ici que les packages {aspe} et
{aspeQual} ont déjà été installés et que les tables de la
base Aspe sont disponibles sous forme de
dataframes sauvegardés au format .RData (sinon
se reporter au tuto dédié à
ces étapes).
Les fonctions du package {aspe} dédiées à ces
traitements sont identifiées par le préfixe qtp_, pour
“Qualité Taille Poids” car l’usage premier de ces fonctions est la mise
en qualité de la base en détectant de potentielles erreurs de mesures ou
de saisie.
Les fonctions de mise en forme du jeu de données en vue de son
traitement sont préfixées par mef_.
Le point de départ est l’hypothèse classique selon laquelle l’équation qui permet de relier la longueur d’un poisson à son poids est de la forme :
\(Poids=a\cdot Longueur^b\)
Les valeurs des coefficients \(a\) et \(b\) dépendent des unités qui sont, dans Aspe, les grammes et les millimètres.
Pour déterminer les valeurs des coefficients sur un jeu de données, les deux termes de l’égalité sont log-transformés pour prendre la forme :
\(\log(Poids)=\log(a)+b\cdot log(Longueur)\)
Il suffit d’effectuer la régression linéaire de \(\log(Poids)\) en fonction de \(log(Longueur)\), de récupérer les coefficients \(\alpha=\log(a)\) et \(\beta=b\), puis de calculer \(a=e^{\alpha}\).
L’analyse reposant sur la régression entre deux grandeurs, il est nécessaires que chacune :
Il faut aussi que les distributions des deux variables soient “raisonnablement” proches d’une gaussienne une fois log-transformées en particulier pour éviter que de potentiels outliers ne perturbent la relation générale.
library(aspe)
library(aspeQual)
library(tidyverse)
load(file = "raw_data/tables_sauf_mei_2021_10_21_11_44_01.RData")
load(file = "raw_data/mei_2021_10_21_11_44_01.RData")
Dans cet exemple, toutes les mesures individuelles de la base sont prises en compte.
NB : Pour produire des relations taille - poids spécifiques à une région, en théorie il suffit de procéder de même après avoir filtré la table
mesure_individuelle. En pratique, certaines espèces rares ou de très petite taille (erreurs relatives importantes de mesure du poids) peuvent bloquer la procédure.
Pour constituer le jeu de données, les étapes sont :
tp_data <- mef_creer_passerelle() %>% # création de la passerelle
mef_ajouter_lots() %>% # ajout des lots
mef_ajouter_type_lot() %>% # ajout du type de lot
filter(tyl_libelle == "N") %>% # type de lot correspondant aux mesures individuelles
mef_ajouter_type_longueur() %>% # ajout du type de longueur
mef_ajouter_esp() %>% # ajout des codes et noms espèces
mef_ajouter_mei() %>% # ajout mesures individuelles
filter(mei_taille > 0 & # taille positive
mei_poids > 0 & # poids positifs
mei_mesure_reelle == "t") %>% # mesure réelle et non obtenue en "dégroupant" des lots
select(mei_id,
lop_id,
tlo_libelle,
mei_taille,
mei_poids,
esp_code_alternatif) %>%
distinct() # dédoublonnage
Combien de couples de mesures par espèce, selon que la longueur mesurée est totale ou à la fourche ?
obs_par_esp <- tp_data %>%
group_by(esp_code_alternatif,
tlo_libelle) %>%
tally() %>%
ungroup() %>%
pivot_wider(names_from = tlo_libelle,
values_from = n) %>%
purrr::set_names(c("Espèce", "L. fourche", "L. totale", "Estimée d'après le poids")) %>%
arrange(-`L. totale`)
obs_par_esp %>%
DT::datatable(rownames = FALSE, width = 400) %>%
DT::formatCurrency(columns = c("L. fourche", "L. totale", "Estimée d'après le poids"),
currency = "",
interval = 3,
mark = " ",
digits = 0)
Dans la suite du document, les longueurs fourche et totale seront traitées séparément.
On se propose ici de détailler toutes les étapes de la construction de la relation taille - poids pour une espèce en restreignant le jeu de données aux longueurs totales.
mon_espece <- "VAN"
esp_lt_data <- tp_data %>%
filter(tlo_libelle == "Totale",
esp_code_alternatif == mon_espece)
La première étape, pour éviter que des outliers ne perturbent les analyses, est de définir pour chaque espèce des gammes de longueurs et de poids “plausibles” en dehors desquelles les observations seront exclues pour effectuer la régression.
La fonction qtp_histo() du package aspeQual
permet de produire les graphiques de distribution des variables de
taille et de poids.
Production du graphique pour :
qtp_histo(df = esp_lt_data,
code_espece = mon_espece,
variable = "mei_taille",
x_max = 500)
Quelle est la gamme de longueurs probables pour la VAN, avec une probabilité de 1% ?
Application de la fonction qtp_seuils(). Elle retourne
un objet contenant deux éléments qui sont les bornes supérieure et
inférieure.
seuils <- aspeQual::qtp_seuils(
df = esp_lt_data,
code_espece = mon_espece,
variable = "mei_taille",
seuil_densite = 0.01
)
seuils
## [1] 14 391
Interprétation : Pour les VAN, avec une densité de probabilité de 1%, on considérera les individus inférieurs à 14mm ou supérieurs à 391mm avec une attention particulière, voire ils seront considérés comme les outliers et éliminés.
Pour accéder au seuil mini ou au maxi, on les sélectionne avec les
chochets [] autour du numéro souhaité. Le première des
valeurs est le mini.
seuils[1]
## [1] 14
La seconde est la maxi.
seuils[2]
## [1] 391
Bien sûr ces valeurs dépendent de la densité de probabilité.
probas <- c(0.1, 0.05, 0.01, 0.001)
map(.x = probas,
.f = qtp_seuils,
df = esp_lt_data,
code_espece = mon_espece,
variable = "mei_taille") %>%
reduce(rbind) %>%
as.data.frame() %>%
purrr::set_names(c("mini", "maxi")) %>%
mutate(densite_proba = probas) %>%
select(densite_proba, everything()) %>%
knitr::kable(row.names = F, format = "html", table.attr = "style='width:40%;'")
| densite_proba | mini | maxi |
|---|---|---|
| 0.100 | 31 | 312 |
| 0.050 | 25 | 335 |
| 0.010 | 14 | 391 |
| 0.001 | 7 | 474 |
Pour plus de détail sur la méthode sous-jacente, taper dans la
console get("compute_group", ggplot2::StatDensity).
Les longueurs dans la base Aspe sont en mm, mais les courbes issues de la littérature et synthétisées dans Fishbase sont pour des longueurs en cm. Afin que les deux soient comparables, nous choisissons ici de convertir les longueurs en cm.
esp_lt_data <- esp_lt_data %>%
mutate(mei_taille = mei_taille / 10)
Pour éviter que les relations taille - poids soient dégradées par des valeurs très peu probables, on les construit sur une gamme de données écartant les valeurs extrêmes.
Détermination des seuils.
seuils_taille <- qtp_seuils(df = esp_lt_data,
code_espece = mon_espece,
variable = "mei_taille")
seuils_poids <- qtp_seuils(df = esp_lt_data,
code_espece = mon_espece,
variable = "mei_poids")
Filtrage en utilisant les seuils.
esp_lt_data <- esp_lt_data %>%
filter(mei_taille > seuils_taille[1],
mei_taille < seuils_taille[2],
mei_poids > seuils_poids[1],
mei_poids < seuils_poids[2],
mei_poids >= 5) # en-dessous de 5g la balance est souvent imprécise
On construit un premier modèle en sachant qu’il est potentiellement fortement influencé par quelques observations. Le but est d’identifier de telles observations pour, si nécessaire, les supprimer avant de construire de nouveau un modèle.
La fonction lm() du package {stats} permet
de réaliser une régression du log du poids en fonction du log de la
longueur.
mod <- lm(log(mei_poids) ~ log(mei_taille),
data = esp_lt_data)
Le modèle mod est un objet de classe “lm” (linear
model). Pour en explorer le contenu, names(mod). Pour
en obtenir le résumé :
summary(mod)
##
## Call:
## lm(formula = log(mei_poids) ~ log(mei_taille), data = esp_lt_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.3917 -0.1137 -0.0003 0.1057 7.0392
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -4.364089 0.014653 -297.8 <2e-16 ***
## log(mei_taille) 2.921969 0.005159 566.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2651 on 21943 degrees of freedom
## Multiple R-squared: 0.936, Adjusted R-squared: 0.936
## F-statistic: 3.208e+05 on 1 and 21943 DF, p-value: < 2.2e-16
Pour obtenir les graphiques de diagnostic :
plot(mod)
Malgré un r² ajusté de 0.93, l’ajustement est médiocre. Quelques outliers contribuent fortement au modèle. Ils ont un très fort ‘bras de levier’ (leverage) comme le montre le graphique du bas.
Pour évaluer l’influence de chacun des individus sur l’équation de la régression, on peut utiliser la distance de Cook. Une cote mal taillée consiste à considérer que si la distance de Cook d’une observation excède 4/N, avec N le nombre d’observations, il est préférable d’éliminer cette observation.
Ces distances sont obtenues en appliquant au modèle la fonction
cooks.distance() du package {stats}.
dist_cook <- cooks.distance(mod)
seuil_cook <- 4 / nrow(esp_lt_data)
esp_lt_data <- esp_lt_data %>%
cbind(dist_cook) %>% # ajout de la colonne avec les distances
filter(dist_cook < seuil_cook) # suppression des observations avec distance > 4/N
On peut maintenant recommencer et déterminer la relation taille - poids sur les observations retenues.
mod <- lm(log(mei_poids) ~ log(mei_taille),
data = esp_lt_data)
summary(mod)
##
## Call:
## lm(formula = log(mei_poids) ~ log(mei_taille), data = esp_lt_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.72842 -0.09889 -0.00147 0.09874 0.72010
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -4.720312 0.009980 -473.0 <2e-16 ***
## log(mei_taille) 3.044877 0.003489 872.8 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1641 on 20972 degrees of freedom
## Multiple R-squared: 0.9732, Adjusted R-squared: 0.9732
## F-statistic: 7.617e+05 on 1 and 20972 DF, p-value: < 2.2e-16
plot(mod)
Les coefficients du modèle sont :
mod$coefficients
## (Intercept) log(mei_taille)
## -4.720312 3.044877
Au final, l’équation retenue est :
log(poids) = log(-4.7203123) + 3.0448774 x log(longueur), soit, en conservant pas mal de décimales :
Poids(g) = 0.008912395 x Longueur(cm)3.0448774
Graphiquement, on peut représenter la relation avec des axes
arithmétiques ou bien log-transformés. Les courbes en rouge indiquent
les valeurs prédites par le modèle. Elles sont contenues dans le modèle
et on y accède par mod$fitted.values. Comme le modèle
prédit \(\log(Poids)\), pour obtenir le
poids prédit on applique une exponentielle avec la fonction
exp().
esp_lt_data <- esp_lt_data %>%
cbind(predicted = exp(mod$fitted.values)) %>% # ajout des valeurs prédites
arrange(mei_taille) # mise en ordre de taille (pour le geom_line())
# graphique en axes arithmétiques
gg1 <- ggplot(data = esp_lt_data,
aes(x = mei_taille,
y = mei_poids)) +
geom_point(size = 0.2) +
geom_line(aes(y = predicted),
col = "red",
size = 1) +
labs(x = "Longueur (mm)",
y = "Masse (g)",
title = mon_espece)
# graphique en axes log-log
gg2 <- gg1 +
scale_x_log10() +
scale_y_log10()
# assemblage des graphiques
ggpubr::ggarrange(gg1,
gg2,
nrow = 1)
La section ci-dessus a détaillé pas à pas la procédure de détermination de la relation taille - poids pour une espèce. Il s’agit maintenant d’aller droit au but pour obtenir le résultat pour plusieurs espèces si l’on se satisfait des options par défaut et qu’aucune difficulté ne vient perturber les calculs.
Conversion des longueurs en cm :
tp_data <- tp_data %>%
mutate(mei_taille = mei_taille / 10)
Séparation du jeu de données selon le type de longueur mesurée :
tp_data_lt <- tp_data %>%
filter(tlo_libelle == "Totale")
tp_data_lf <- tp_data %>%
filter(tlo_libelle == "Fourche")
resume <- qtp_resume_donnees(df = tp_data_lt,
seuil_poids_absolu = 5)
resume %>%
mutate_if(is.numeric, round, 1) %>%
DT::datatable(rownames = FALSE)
Les espèces pour lesquelles seuls quelques individus passent le seuil du poids minimum ne peuvent faire l’objet d’une analyse fiable. Sélectionnons par exemple les espèces avec au minimum 20 individus dépassant le seuil.
mes_especes <- resume %>%
filter(n_sup_seuil > 19) %>%
pull(esp_code_alternatif) %>%
as.character()
mes_especes
## [1] "ASP" "HBG" "LPR" "ALA" "CAR" "CAX" "GOL" "GKS" "PLI" "EPI" "CCU" "LOU"
## [13] "OBL" "TRM" "APR" "BAX" "MUC" "LOR" "LPX" "MGL" "TRL" "BOU" "CDR" "LPM"
## [25] "IDE" "CAD" "GTN" "PAP" "FLE" "SDF" "PSR" "VAR" "BBG" "CMI" "BLE" "CAG"
## [37] "MUP" "LOE" "SAN" "PCC" "LOT" "CAS" "VAI" "GRE" "TAC" "LPP" "CCO" "OBR"
## [49] "APP" "OCL" "SPI" "PCH" "ROT" "SIL" "PFL" "TAN" "BRB" "BRE" "TOX" "LOF"
## [61] "ABL" "BRO" "PES" "BAM" "HOT" "CHA" "BLN" "VAN" "PER" "GOU" "BAF" "GAR"
## [73] "ANG" "SAT" "CHE" "TRF"
Au total, 76 espèces comptent suffisamment d’individus pesés et mesurés en longueur totale.
Sur ces espèces, on peut appliquer la fonction
qtp_calcul(). Pour en savoir plus sur cette fonction,
exécuter dans la console ?qtp_calcul. Ici elle est employée
avec ses options par défaut, ce qui implique que les individus pesant
moins de 5g sont écartés ainsi que ceux présentant des valeurs extrêmes
de taille ou de poids.
tp_lt <- qtp_calcul(df = tp_data_lt,
especes = mes_especes)
tp_lt %>%
DT::datatable(rownames = FALSE,
options = list(columnDefs = list(list(className = 'dt-center',
targets = 0:3)))) %>%
DT::formatRound(columns = c('a', 'b'), digits = 4) %>%
DT::formatRound(columns = c('r2_ajuste'), digits = 2) %>%
DT::formatRound(columns = c('taille_mini', 'taille_maxi', 'poids_mini'), digits = 1) %>%
DT::formatCurrency(columns = c('n_ind', 'poids_maxi'), currency = "",
interval = 3, mark = " ", digits = 0)
Certaine des \(r^2\) ajustés ne font pas rêver. On va considérer que s’il est inférieur à 0.8, la relation n’est pas utilisable.
tp_lt <- tp_lt %>%
filter(r2_ajuste > 0.8) %>%
mutate(tlo_libelle = "Totale")
On répète les opérations détaillées pour les longueurs totales.
resume <- qtp_resume_donnees(df = tp_data_lf,
seuil_poids_absolu = 5)
Espèces avec au minimum 20 individus dépassant le seuil de 5g :
mes_especes <- resume %>%
filter(n_sup_seuil > 19) %>%
pull(esp_code_alternatif) %>%
as.character()
mes_especes
## [1] "BOU" "CAD" "CAG" "LOU" "CMI" "PSR" "BBG" "SDF" "SPI" "SAN" "VAI" "VAR"
## [13] "TAC" "TOX" "TRM" "MUP" "CAS" "ROT" "BRB" "CCO" "TAN" "GRE" "BAM" "BRE"
## [25] "ABL" "HOT" "BLN" "PES" "BRO" "OBR" "BAF" "PER" "VAN" "GOU" "SAT" "GAR"
## [37] "CHE" "TRF"
38 espèces comptent suffisamment d’individus pesés et mesurés en longueur fourche.
Application de la fonction qtp_calcul() aux espèces
sélectionnées.
tp_lf <- qtp_calcul(df = tp_data_lf,
especes = mes_especes) %>%
filter(r2_ajuste > 0.8) %>% # Suppression des r2 ajustés inférieurs à 0.8
mutate(tlo_libelle = "Fourche")
tp_aspe <- rbind(tp_lf, tp_lt)
rownames(tp_aspe) <- NULL
tp_aspe %>%
DT::datatable(rownames = FALSE,
options = list(columnDefs = list(list(className = 'dt-center',
targets = 0:3)))) %>%
DT::formatRound(columns = c('a', 'b'), digits = 4) %>%
DT::formatRound(columns = c('r2_ajuste'), digits = 2) %>%
DT::formatRound(columns = c('taille_mini', 'taille_maxi', 'poids_mini'), digits = 1) %>%
DT::formatCurrency(columns = c('n_ind', 'poids_maxi'), currency = "",
interval = 3, mark = " ", digits = 0)
tp_aspe %>%
downloadthis::download_this(
output_name = "taille_poids",
output_extension = ".csv",
button_label = "Télécharger le tableau en csv",
button_type = "success",
has_icon = TRUE,
icon = "fa fa-save"
)
Le package {rfishbase} (Boettiger, Lang & Wainwright 2012) est une interface pour utiliser avec R les données compilées dans Fishbase sur plusieurs milliers d’espèces.
Il a été et a largement enrichi depuis 2012 pour suivre les évolutions de Fishbase et est accompagné d’un tutoriel.
On peut donc comparer nos résultats avec des données de Fishbase.
{rfishbase}Il faut d’abord charger le package {rfishbase} pour accéder à sa
fonction length_weight().
install.packages("rfishbase")
Exemple avec la tanche, Tinca tinca (la fonction permettrait de charger les relations pour plusieurs espèces à la fois).
Une fois les données téléchargées, les colonnes sont renommées et les modalités de la longueur sont recodées en Totale ou Fourche pour garder la cohérence avec Aspe. Les autres modalités n’étant pas présentes dans Aspe, elles sont écartées (ex : longueur standard).
nom_latin <- "Tinca tinca" # choix de l'espèce
esp_fb_data <- rfishbase::length_weight(nom_latin) %>% # téléchargement
select(tlo_libelle = Type, # sélection et renommage
a,
b,
r2_ajuste = CoeffDetermination,
n_ind = Number,
taille_mini = LengthMin,
taille_maxi = LengthMax,
lieu = Locality) %>%
mutate(tlo_libelle = case_when(tlo_libelle == "TL" | is.na(tlo_libelle) ~ "Totale", # recodage
tlo_libelle == "FL" ~ "Fourche")) %>%
filter(tlo_libelle %in% c("Totale", "Fourche")) # filtrage
Création d’un objet lieu pour localiser notre étude.
lieu <- data.frame(lieu = "France - ASPE")
Pour assembler nos résultats et ceux de Fishbase, on empile les tableaux. La présente étude figure sur la première ligne.
esp_synthese <- tp_aspe %>%
filter(esp_code_alternatif == "TAN") %>% # seulement la tanche
cbind(lieu) %>% # ajout du lieu comme une colonne
select(names(esp_fb_data)) %>% # agencement des colonnes dans le même ordre que celles de fb_data
rbind(esp_fb_data) # empilement
Comme les intitulés des lieux d’étude sont assez moches (parfois les
années, les coordonnées etc.) on peut les nettoyer un peu en utilisant
la fonction qtp_nettoyer_fb_lieu() qui va supprimer dans la
variable lieu les chiffres, les contenus entre parenthèses,
les coordonnées ainsi que divers caractères spéciaux.
esp_synthese <- esp_synthese %>%
qtp_nettoyer_fb_lieu()
Tableau mise en forme pour la tanche :
esp_synthese %>%
DT::datatable(rownames = FALSE) %>%
DT::formatRound(columns = c('a', 'b'), digits = 4) %>%
DT::formatRound(columns = c('r2_ajuste'), digits = 2) %>%
DT::formatRound(columns = c('taille_mini', 'taille_maxi'), digits = 1) %>%
DT::formatCurrency(columns = c('n_ind'), currency = "",
interval = 3, mark = " ", digits = 0)
On peut reproduire le type de graphique que propose Fishbase pour situer les relations taille - poids les unes par rapport aux autres.
g <- ggplot(data = esp_synthese,
aes(x = b,
y = log(a, base = 10),
text = lieu)) +
geom_point() +
geom_point(data = esp_synthese[1,],
color = "red",
size = 2) +
geom_text(data = esp_synthese[1,],
color = "red",
label = "France - Aspe",
nudge_y = 0.07) +
labs(y = "log10(a)",
title = nom_latin)
plotly::ggplotly(g, tooltip = "text")
Il est rassurant que notre point ne tombe pas totalement en dehors du nuage de points. Le point en bas à gauche est pour le moins douteux.
Il s’agit ici de compléter les relations obtenues à partir de Aspe par celles de Fishbase pour obtenir les équations de conversions pour toutes les espèces de poissons de la base Aspe. Le tableau ainsi obtenu permettra donc d’évaluer les poids de tous les individus ou lots qui n’ont pas été pesés.
Comme la requête depuis Fishbase requiert les noms scientifiques des
espèces au format genre espèce en deux mots, on prépare la
liste des noms à partir du dataframe resume en
supprimant ce qui est entre parenthèses et au-delà du deuxième mot. Par
exemple Salmo trutta fario devient Salmo trutta.
Quand la détermination a été approximée au genre, c’est qu’il y a hésitation entre plusieurs espèces morphologiquement très proches. Pour la plupart des usages classiques il n’est pas génant de prendre la relation taille - poids d’une des espèces possible. Ainsi, on recode les Carassius déterminés seulement au genre en Carassius carassius. On procède de même pour les (rares) taxons pour lesquels aucune donnée taille-poids n’est publiée sur Fishbase (ex : Gobio occitaniae, Pungitius pungitius) en longueur fourche ou totale.
especes <- tp_data %>%
select(esp_code_alternatif) %>%
mef_ajouter_esp() %>%
select(esp_code_alternatif, esp_nom_latin) %>%
distinct() %>%
mutate(esp_nom_latin = str_replace(esp_nom_latin, # suppression du contenu entre parenthèses
pattern = " \\s*\\([^\\)]+\\)",
replacement = ""),
esp_nom_latin_n_mots = str_count(esp_nom_latin, "\\S+"), # calcul du nb de mots
esp_nom_latin = ifelse(esp_nom_latin_n_mots == 1, # suppression des mots au-delà du 2e
yes = word(esp_nom_latin, 1, 1, sep = " "),
no = word(esp_nom_latin, 1, 2, sep = " ")),
esp_nom_latin = case_when(
esp_nom_latin == "Blicca" ~ "Blicca bjoerkna", # détermination au genre
esp_nom_latin == "Abramis" ~ "Abramis brama",
esp_nom_latin == "Barbus" ~ "Barbus barbus",
esp_nom_latin == "Carassius" ~ "Carassius carassius",
esp_nom_latin == "Coregonus" ~ "Coregonus lavaretus",
esp_nom_latin == "Cyprinidae" ~ "Rutilus rutilus",
esp_nom_latin == "Gobio" ~ "Gobio gobio",
esp_nom_latin == "Lampetra" ~ "Lampetra fluviatilis",
esp_nom_latin == "Micropterus" ~ "Micropterus salmoides",
esp_nom_latin == "Phoxinus" ~ "Phoxinus phoxinus",
esp_nom_latin == "Liza aurata" ~ "Chelon auratus", # espèce renommée
esp_nom_latin == "Gobio occitaniae" ~ "Gobio gobio", # recodage par espèce proche
esp_nom_latin == "Cobitis bilineata" ~ "Cobitis taenia", # idem
esp_nom_latin == "Proterorhinus semilunaris" ~ "Proterorhinus marmoratus", # idem
esp_nom_latin == "Pungitius pungitius" ~ "Gasterosteus aculeatus", # idem
TRUE ~ esp_nom_latin),
esp_nom_latin = str_squish(esp_nom_latin)) %>% # suppression des espaces qui trainent
select(-esp_nom_latin_n_mots)
Ensuite on collecte les paramètres depuis Fishbase. Les données taguées douteuses sont supprimées, sauf pour Telestes souffia car il n’y a qu’une donnée sur Fishbase. Faute de mieux on la conserve.
tp_fb <- especes %>%
pull(esp_nom_latin) %>%
unique() %>%
rfishbase::length_weight() %>%
filter(is.na(EsQ) | Species == "Telestes souffia") %>% # suppression données taguées douteuses
select(esp_nom_latin = Species,
tlo_libelle = Type,
a,
b,
r2_ajuste = CoeffDetermination,
n_ind = Number,
taille_mini = LengthMin,
taille_maxi = LengthMax,
lieu = Locality) %>%
mutate(tlo_libelle = case_when(tlo_libelle == "TL" | is.na(tlo_libelle) ~ "Totale",
tlo_libelle == "FL" ~ "Fourche")) %>%
filter(tlo_libelle %in% c("Totale", "Fourche")) %>% # on ne garde que les longueurs fourche ou totale
qtp_nettoyer_fb_lieu() # nettoyage du champ "lieu"
A partir de ce tableau, on agrège par espèce et type de longueur.
NB La relation taille - poids étant de type puissance, on retient pour les paramètres \(a\) et \(b\) “moyens” respectivement les moyennes géométrique et arithmétique (méthode retenue par Fishbase).
tp_fb <- tp_fb %>%
group_by(esp_nom_latin, tlo_libelle) %>%
summarise(a = exp(mean(log(a))), # moyenne géométrique
b = mean(b), # moyenne arithmétique
n_etudes = n(), # nb d'études
source = "Fishbase") %>%
left_join(y = especes) %>%
select(esp_code_alternatif,
esp_nom_latin,
tlo_libelle,
a,
b,
source,
n_etudes) %>%
filter(!is.na(a))
Mise en forme du tableau tp_aspe pour qu’il puisse être
superposé avec tp_fb (colonnes dans le même ordre).
tp_aspe <- tp_aspe %>%
mutate(source = "ASPE",
n_etudes = 1) %>%
left_join(y = ref_espece %>%
select(esp_code_alternatif, esp_nom_latin)) %>%
select(names(tp_fb))
Les deux tableaux sont superposés.
tp <- rbind(tp_fb, tp_aspe) %>%
arrange(esp_code_alternatif, tlo_libelle)
Pour contrôler si le tableau tp contient au moins une
relation taille-poids pour chacune des espèces de poisson de la base
Aspe, on peut chercher les esp_code_alternatif présents
dans tp_data mais qui seraient absents de
tp.
A ce stade, on dispose donc d’un tableau contenant a minima une relation de conversion taille - poids pour chacune des espèces de poissons (plus pour quelques crustacés) recensées dans la base Aspe. Pour certaines espèces comme Barbus meridionalis on dispose de 4 relations, soit deux issues de Fishbase (Lt et Lf) et deux issues de Aspe.
save(tp, file = "processed_data/tp.RData")