ECONSHAB Peixos 2026 – Tècnica Visual

Grau de Ciències del Mar · Universitat de Barcelona

Autor/a

Pràctiques d’Ecologia Marina

Publicat

14 de maig de 2026

Com fer servir aquest document

Executa cada bloc de codi en ordre, de dalt a baix. Llegeix les explicacions de cada secció abans d’executar el codi. Tots els gràfics es guardaran automàticament a la carpeta grafics_visual/.


1 Càrrega i preparació de dades

1.1 Importació del fitxer CSV

El primer pas és sempre carregar les dades i revisar-ne l’estructura. Usem read.csv2() perquè el fitxer utilitza ; com a separador (format europeu) i , com a separador decimal.

Mostra/amaga el codi
# Llegim el CSV i netegem l'encoding
dades_raw <- read.csv2(
  "ECONSHAB_Peixos_2026.csv",
  stringsAsFactors = FALSE,
  fileEncoding     = "latin1"
) %>%
  mutate(across(where(is.character), \(x) iconv(x, from = "latin1", to = "UTF-8")))

# Comprovem les primeres files i l'estructura
glimpse(dades_raw)
Rows: 1,621
Columns: 29
$ tecnica       <chr> "Visual", "Visual", "Visual", "Visual", "Visual", "Visua…
$ observador    <chr> "Marina Saiz", "Marina Saiz", "Marina Saiz", "Marina Sai…
$ any           <int> 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 20…
$ grup          <chr> "D1", "D1", "D1", "D1", "D1", "D1", "D1", "D1", "D1", "D…
$ data          <chr> "4/5/2026", "4/5/2026", "4/5/2026", "4/5/2026", "4/5/202…
$ parc          <chr> "Begur", "Begur", "Begur", "Begur", "Begur", "Begur", "B…
$ estacio       <chr> "Aigua freda", "Aigua freda", "Aigua freda", "Aigua fred…
$ proteccio     <chr> "NP", "NP", "NP", "NP", "NP", "NP", "NP", "NP", "NP", "N…
$ comptatge     <int> 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,…
$ transsecte    <chr> "Marina Saiz1", "Marina Saiz1", "Marina Saiz1", "Marina …
$ especie       <chr> "Diplodus vulgaris", "Diplodus puntazzo", "Sarpa salpa",…
$ familia       <chr> "Sparidae", "Sparidae", "Sparidae", "Serranidae", "Labri…
$ talla         <chr> "P", "M", "P", "P", "P", "P", "P", "P", "P", "P", "M", "…
$ talla.mediana <int> 7, 22, 7, 5, 5, 5, 7, 5, 5, 7, 22, 22, 4, 22, 7, 22, 5, …
$ nombre        <int> 22, 2, 2, 7, 6, 5, 2, 4, 1, 26, 4, 2, 4, 2, 20, 60, 1, 2…
$ a             <dbl> 0.0149, 0.0260, 0.0323, 0.0104, 0.0076, 0.0076, 0.0076, …
$ b             <dbl> 3.0058, 2.8188, 2.7004, 3.1103, 3.1862, 3.1862, 3.1862, …
$ biomassa      <dbl> 113.711565, 316.245530, 12.369019, 10.867704, 7.691713, …
$ prof          <dbl> 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3,…
$ pendent       <dbl> 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2,…
$ rugositat     <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ megablocs     <int> 50, 50, 50, 50, 50, 20, 20, 20, 20, 20, 20, 20, 20, 20, …
$ blocs_mitjans <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ blocs_petits  <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ roca_base     <int> 50, 50, 50, 50, 50, 80, 80, 80, 80, 80, 80, 80, 80, 80, …
$ coraligen     <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ pedres        <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ sorra         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ posidonia     <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…

1.2 Neteja i transformació de variables

Convertim les columnes numèriques, unifiquem les etiquetes de les tècniques, i convertim els factors ordenats.

Mostra/amaga el codi
dades_raw <- dades_raw %>%
  mutate(
    tecnica = case_when(
      tolower(trimws(tecnica)) == "visual" ~ "visual",
      tolower(trimws(tecnica)) == "bruv"             ~ "BRUV",
      TRUE ~ tecnica
    ),
    nombre        = as.numeric(nombre),
    biomassa      = as.numeric(biomassa),
    talla.mediana = as.numeric(gsub(",", ".", talla.mediana)),
    across(prof:posidonia, \(x) as.numeric(gsub(",", ".", x))),
    talla     = factor(talla, levels = c("P", "M", "G"),
                       labels = c("Petits", "Mitjans", "Grans")),
    proteccio = factor(proteccio, levels = c("AMP", "NP")),
    estacio   = factor(stringr::str_to_title(trimws(estacio)),
                       levels = c("Aigua Freda", "Sa Tuna", "Guix",
                                  "Embarcador", "Cementiri"))
  )

# Filtrem NOMÉS la tècnica Visual per a aquest document
dades <- dades_raw %>% filter(tecnica == "visual")

cat(sprintf("Observacions tècnica Visual: %d\n", nrow(dades)))
Observacions tècnica Visual: 1505
Mostra/amaga el codi
cat(sprintf("Espècies: %d\n",     n_distinct(dades$especie)))
Espècies: 23
Mostra/amaga el codi
cat(sprintf("Transsectes: %d\n",  n_distinct(dades$transsecte)))
Transsectes: 127
Mostra/amaga el codi
cat(sprintf("Estacions: %d\n",    n_distinct(dades$estacio)))
Estacions: 5

2 Exploració de dades

L’exploració inicial ens permet entendre l’estructura del dataset, identificar valors mancants i detectar possibles outliers abans de fer cap anàlisi.

2.1 Resum estadístic

Mostra/amaga el codi
# Resum de les variables numèriques principals
resum <- dades %>%
  select(nombre, biomassa, talla.mediana, prof, pendent, rugositat) %>%
  summary()

print(resum)
     nombre           biomassa        talla.mediana        prof        
 Min.   :  1.000   Min.   :    1.26   Min.   : 4.00   Min.   :    2.0  
 1st Qu.:  1.000   1st Qu.:   15.51   1st Qu.: 7.00   1st Qu.:    2.0  
 Median :  3.000   Median :  149.80   Median :15.00   Median :    2.5  
 Mean   :  8.828   Mean   :  850.13   Mean   :16.93   Mean   : 1319.7  
 3rd Qu.:  6.000   3rd Qu.:  645.87   3rd Qu.:22.00   3rd Qu.:    3.0  
 Max.   :300.000   Max.   :34058.13   Max.   :70.00   Max.   :46115.0  
    pendent        rugositat      
 Min.   :1.000   Min.   :    1.5  
 1st Qu.:1.000   1st Qu.:    2.0  
 Median :1.500   Median :    2.0  
 Mean   :1.517   Mean   :  798.4  
 3rd Qu.:1.500   3rd Qu.:    2.5  
 Max.   :3.000   Max.   :46083.0  
Mostra/amaga el codi
# Resum per grup de protecció
dades %>%
  mutate(densitat = nombre / 250) %>%
  group_by(proteccio) %>%
  summarise(
    n_obs        = n(),
    dens_mitja   = round(mean(densitat,  na.rm = TRUE), 4),
    dens_sd      = round(sd(densitat,    na.rm = TRUE), 4),
    bio_mitja    = round(mean(biomassa,  na.rm = TRUE), 2),
    bio_sd       = round(sd(biomassa,    na.rm = TRUE), 2),
    n_especies   = n_distinct(especie),
    .groups = "drop"
  ) %>%
  kable(caption = "Resum per grau de protecció (Visual)",
        col.names = c("Protecció", "N obs.", "Dens. mitja", "Dens. SD",
                      "Bio. mitja (g)", "Bio. SD", "N espècies")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Resum per grau de protecció (Visual)
Protecció N obs. Dens. mitja Dens. SD Bio. mitja (g) Bio. SD N espècies
AMP 1159 0.0336 0.0878 919.37 2450.99 21
NP 346 0.0412 0.0896 618.19 2604.51 17

2.2 Boxplot densitat AMP i NP

Consell

Què veiem? El boxplot per espècie permet comparar la distribució de la densitat dins de cada grup de protecció. Les caixes representen el rang interquartílic (IQR), la línia central la mediana, i els punts els outliers.

Mostra/amaga el codi
# Preparem les dades de densitat
dades_dens <- dades %>% mutate(densitat = nombre / 250)

# Boxplot per AMP
p_box_dens_AMP <- dades_dens %>%
  filter(proteccio == "AMP") %>%
  ggplot(aes(x = reorder(especie, densitat, FUN = median),
             y = densitat, fill = especie)) +
  geom_boxplot(outlier.shape = 21, outlier.size = 1.5, show.legend = FALSE) +
  labs(title    = "Densitat per espècie – AMP (Àrea Marina Protegida)",
       subtitle = "Tècnica: Comptatge visual | Unitat: ind/250m²",
       x = NULL, y = "Densitat (ind/250m²)") +
  theme_bw(base_size = 12) +
  theme(axis.text.x  = element_text(angle = 45, hjust = 1, face = "italic"),
        panel.grid.major.x = element_blank())

print(p_box_dens_AMP)

Mostra/amaga el codi
desa_grafic("01a_boxplot_densitat_AMP.png", p_box_dens_AMP, ample = 12, alt = 7)
Mostra/amaga el codi
# Boxplot per NP
p_box_dens_NP <- dades_dens %>%
  filter(proteccio == "NP") %>%
  ggplot(aes(x = reorder(especie, densitat, FUN = median),
             y = densitat, fill = especie)) +
  geom_boxplot(outlier.shape = 21, outlier.size = 1.5, show.legend = FALSE) +
  labs(title    = "Densitat per espècie – NP (No Protegit)",
       subtitle = "Tècnica: Comptatge visual | Unitat: ind/250m²",
       x = NULL, y = "Densitat (ind/250m²)") +
  theme_bw(base_size = 12) +
  theme(axis.text.x  = element_text(angle = 45, hjust = 1, face = "italic"),
        panel.grid.major.x = element_blank())

print(p_box_dens_NP)

Mostra/amaga el codi
desa_grafic("01b_boxplot_densitat_NP.png", p_box_dens_NP, ample = 12, alt = 7)

2.3 Boxplot biomassa AMP i NP

Mostra/amaga el codi
dades_bio <- dades %>% mutate(biomassa_250 = biomassa / 250)

p_box_bio_AMP <- dades_bio %>%
  filter(proteccio == "AMP") %>%
  ggplot(aes(x = reorder(especie, biomassa_250, FUN = median),
             y = biomassa_250, fill = especie)) +
  geom_boxplot(outlier.shape = 21, outlier.size = 1.5, show.legend = FALSE) +
  labs(title = "Biomassa per espècie – AMP",
       x = NULL, y = "Biomassa (g/250m²)") +
  theme_bw(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, face = "italic"),
        panel.grid.major.x = element_blank())

p_box_bio_NP <- dades_bio %>%
  filter(proteccio == "NP") %>%
  ggplot(aes(x = reorder(especie, biomassa_250, FUN = median),
             y = biomassa_250, fill = especie)) +
  geom_boxplot(outlier.shape = 21, outlier.size = 1.5, show.legend = FALSE) +
  labs(title = "Biomassa per espècie – NP",
       x = NULL, y = "Biomassa (g/250m²)") +
  theme_bw(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, face = "italic"),
        panel.grid.major.x = element_blank())

print(p_box_bio_AMP)

Mostra/amaga el codi
print(p_box_bio_NP)

Mostra/amaga el codi
desa_grafic("01c_boxplot_biomassa_AMP.png", p_box_bio_AMP, ample = 12, alt = 7)
desa_grafic("01d_boxplot_biomassa_NP.png",  p_box_bio_NP,  ample = 12, alt = 7)

3 Diversitat

Conceptes clau
  • S (riquesa): nombre d’espècies observades per transsecte.
  • H’ (Shannon): mesura la diversitat tenint en compte la riquesa i l’equitativitat. Valors alts → comunitat diversa i equilibrada.
  • J’ (Pielou): equitativitat normalitzada entre 0 i 1. J’ = H’ / ln(S). Valors propers a 1 → les espècies estan distribuïdes uniformement.

3.1 Preparació de la matriu d’espècies × transsecte

Mostra/amaga el codi
# Agreguem per transsecte i espècie
trans_sp <- dades %>%
  group_by(proteccio, estacio, transsecte, especie) %>%
  summarise(N_total = sum(nombre, na.rm = TRUE), .groups = "drop")

# Matriu ampla (transsectes × espècies)
mat_div <- trans_sp %>%
  pivot_wider(names_from = especie, values_from = N_total, values_fill = 0)

mat_num <- mat_div %>% select(-proteccio, -estacio, -transsecte) %>% as.matrix()
rownames(mat_num) <- mat_div$transsecte

3.2 Nombre d’espècies (S) per transsecte

Mostra/amaga el codi
riquesa_df <- trans_sp %>%
  group_by(proteccio, transsecte) %>%
  summarise(S = n_distinct(especie[N_total > 0]), .groups = "drop")

# Taula resum
riquesa_df %>%
  group_by(proteccio) %>%
  summarise(
    S_mitja  = round(mean(S), 2),
    S_sd     = round(sd(S), 2),
    S_min    = min(S),
    S_max    = max(S),
    n_trans  = n(),
    .groups  = "drop"
  ) %>%
  kable(caption = "Riquesa d'espècies (S) per grau de protecció",
        col.names = c("Protecció", "Mitja S", "SD", "Mín", "Màx", "N transsectes")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Riquesa d'espècies (S) per grau de protecció
Protecció Mitja S SD Mín Màx N transsectes
AMP 8.17 2.01 4 13 87
NP 6.35 1.12 4 9 40
Mostra/amaga el codi
p_riquesa <- ggplot(riquesa_df, aes(x = proteccio, y = S, fill = proteccio)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 21) +
  geom_jitter(width = 0.1, size = 2.5, alpha = 0.7) +
  scale_fill_manual(values = colors_prot) +
  labs(title    = "Riquesa d'espècies (S) per transsecte",
       subtitle = "Cada punt = un transsecte",
       x = NULL, y = "Nombre d'espècies (S)") +
  theme_bw(base_size = 13) + theme(legend.position = "none")

print(p_riquesa)

Mostra/amaga el codi
desa_grafic("02a_boxplot_riquesa_S.png", p_riquesa, ample = 6, alt = 5)

3.3 Índex de Shannon (H’) i equitativitat de Pielou (J’)

Mostra/amaga el codi
H  <- diversity(mat_num, index = "shannon")
S  <- specnumber(mat_num)
J  <- H / log(S)

div_df <- tibble(
  transsecte = rownames(mat_num),
  H_shannon  = H,
  S          = S,
  J_pielou   = J
) %>%
  left_join(trans_sp %>% distinct(transsecte, proteccio, estacio), by = "transsecte")

# Taula resum
div_df %>%
  group_by(proteccio) %>%
  summarise(
    H_mitja  = round(mean(H_shannon, na.rm = TRUE), 3),
    H_sd     = round(sd(H_shannon,   na.rm = TRUE), 3),
    J_mitja  = round(mean(J_pielou,  na.rm = TRUE), 3),
    J_sd     = round(sd(J_pielou,    na.rm = TRUE), 3),
    .groups  = "drop"
  ) %>%
  kable(caption = "Índexs de diversitat per grau de protecció",
        col.names = c("Protecció", "H' mitja", "H' SD", "J' mitja", "J' SD")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Índexs de diversitat per grau de protecció
Protecció H' mitja H' SD J' mitja J' SD
AMP 1.431 0.432 0.689 0.19
NP 1.299 0.312 0.713 0.17
Mostra/amaga el codi
p_shannon <- ggplot(div_df, aes(x = proteccio, y = H_shannon, fill = proteccio)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 21) +
  geom_jitter(width = 0.1, size = 2.5, alpha = 0.7) +
  scale_fill_manual(values = colors_prot) +
  labs(title = "Índex de Shannon (H')", x = NULL, y = "H'") +
  theme_bw(base_size = 13) + theme(legend.position = "none")

p_pielou <- ggplot(div_df, aes(x = proteccio, y = J_pielou, fill = proteccio)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 21) +
  geom_jitter(width = 0.1, size = 2.5, alpha = 0.7) +
  scale_fill_manual(values = colors_prot) +
  labs(title = "Equitativitat de Pielou (J')", x = NULL, y = "J'") +
  theme_bw(base_size = 13) + theme(legend.position = "none")

p_shannon + p_pielou

Mostra/amaga el codi
desa_grafic("02b_boxplot_shannon.png",  p_shannon, ample = 5, alt = 5)
desa_grafic("02c_boxplot_pielou.png",   p_pielou,  ample = 5, alt = 5)

3.4 Tests estadístics de comparació

Consell

Per quin test optem? Primer comprovem la normalitat (Shapiro-Wilk). Si p < 0.05 (dades no normals), usem el test de Wilcoxon (2 grups) o Kruskal-Wallis (> 2 grups). Si les dades són normals, podríem usar una t de Student o ANOVA.

Mostra/amaga el codi
# Test de Shapiro-Wilk per grup
map_dfr(c("S", "H_shannon", "J_pielou"), function(var) {
  map_dfr(c("AMP", "NP"), function(prot) {
    vals <- div_df %>% filter(proteccio == prot) %>% pull(!!sym(var))
    vals <- vals[!is.na(vals)]
    sw   <- tryCatch(shapiro.test(vals), error = \(e) NULL)
    tibble(Variable  = var,
           Protecció = prot,
           n         = length(vals),
           W         = if (!is.null(sw)) round(sw$statistic, 3) else NA,
           p_Shapiro = if (!is.null(sw)) round(sw$p.value,   4) else NA,
           Normal    = if (!is.null(sw)) ifelse(sw$p.value >= 0.05, "Sí", "No") else "—")
  })
}) %>%
  kable(caption = "Test de Shapiro-Wilk: normalitat per índex i grup") %>%
  kable_styling(bootstrap_options = c("striped", "hover")) %>%
  column_spec(6, background = ifelse(. == "No", "#FFCDD2", "#C8E6C9"))
Test de Shapiro-Wilk: normalitat per índex i grup
Variable Protecció n W p_Shapiro Normal
S AMP 87 0.965 0.0188 No
S NP 40 0.923 0.0098 No
H_shannon AMP 87 0.923 0.0001 No
H_shannon NP 40 0.955 0.1166
J_pielou AMP 87 0.931 0.0002 No
J_pielou NP 40 0.909 0.0036 No
Mostra/amaga el codi
# Test de Wilcoxon / Kruskal-Wallis (AMP vs NP)
cat("=== Test de Wilcoxon (AMP vs NP) ===\n\n")
=== Test de Wilcoxon (AMP vs NP) ===
Mostra/amaga el codi
for (var in c("S", "H_shannon", "J_pielou")) {
  amp_vals <- div_df %>% filter(proteccio == "AMP") %>% pull(!!sym(var))
  np_vals  <- div_df %>% filter(proteccio == "NP")  %>% pull(!!sym(var))
  cat(sprintf("-- %s --\n", var))
  cat(sprintf("  AMP: n=%d, mediana=%.3f\n", length(amp_vals), median(amp_vals, na.rm = TRUE)))
  cat(sprintf("  NP:  n=%d, mediana=%.3f\n", length(np_vals),  median(np_vals,  na.rm = TRUE)))
  tryCatch(
    print(wilcox.test(amp_vals, np_vals, exact = FALSE)),
    error = \(e) cat("  Test no disponible\n")
  )
  cat("\n")
}
-- S --
  AMP: n=87, mediana=8.000
  NP:  n=40, mediana=6.000

    Wilcoxon rank sum test with continuity correction

data:  amp_vals and np_vals
W = 2718.5, p-value = 2.689e-07
alternative hypothesis: true location shift is not equal to 0


-- H_shannon --
  AMP: n=87, mediana=1.539
  NP:  n=40, mediana=1.344

    Wilcoxon rank sum test with continuity correction

data:  amp_vals and np_vals
W = 2240, p-value = 0.009526
alternative hypothesis: true location shift is not equal to 0


-- J_pielou --
  AMP: n=87, mediana=0.719
  NP:  n=40, mediana=0.757

    Wilcoxon rank sum test with continuity correction

data:  amp_vals and np_vals
W = 1630, p-value = 0.5698
alternative hypothesis: true location shift is not equal to 0
Mostra/amaga el codi
# Violin plot dels tres índexs
div_long <- div_df %>%
  pivot_longer(cols = c(S, H_shannon, J_pielou),
               names_to = "index", values_to = "valor") %>%
  mutate(index = factor(index,
                        levels = c("S", "H_shannon", "J_pielou"),
                        labels = c("Riquesa (S)", "Shannon (H')", "Pielou (J')")))

# Afegim etiquetes de significació
sig_df <- map_dfr(unique(div_long$index), function(idx) {
  sub <- div_long %>% filter(index == idx)
  amp <- sub %>% filter(proteccio == "AMP") %>% pull(valor)
  np  <- sub %>% filter(proteccio == "NP")  %>% pull(valor)
  p   <- tryCatch(wilcox.test(amp, np, exact = FALSE)$p.value, error = \(e) NA)
  tibble(index = idx,
         y_pos = max(sub$valor, na.rm = TRUE) * 1.05,
         etiq  = sig_label(p))
})

p_violin <- ggplot(div_long, aes(x = proteccio, y = valor, fill = proteccio)) +
  geom_violin(alpha = 0.6, trim = FALSE) +
  geom_boxplot(width = 0.15, outlier.shape = NA, alpha = 0.8) +
  geom_jitter(width = 0.06, size = 1.8, alpha = 0.7) +
  geom_text(data = sig_df, aes(x = 1.5, y = y_pos, label = etiq),
            size = 5, inherit.aes = FALSE) +
  facet_wrap(~index, scales = "free_y") +
  scale_fill_manual(values = colors_prot) +
  labs(title    = "Comparació dels índexs de diversitat (AMP vs NP)",
       subtitle = "* p<0.05 | ** p<0.01 | *** p<0.001 | ns = no significatiu",
       x = NULL, y = "Valor") +
  theme_bw(base_size = 13) + theme(legend.position = "none")

print(p_violin)

Mostra/amaga el codi
desa_grafic("02d_violin_diversitat.png", p_violin, ample = 12, alt = 5)

4 Corba d’acumulació d’espècies

Què és la corba d’acumulació?

La corba d’acumulació mostra com augmenta el nombre d’espècies a mesura que afegim més transsectes (mostrejos). Una corba que s’estabilitza indica que el mostreig és representatiu. S’usa la rarefacció (aleatorització de l’ordre dels transsectes, 999 vegades) per obtenir la corba esperada i la variabilitat (±SD).

Mostra/amaga el codi
# Funció per calcular la corba amb permutacions
calcula_acumulacio <- function(mat, n_perm = 999) {
  n <- nrow(mat)
  mat_sp <- specaccum(mat, method = "random", permutations = n_perm)
  tibble(
    n_trans = mat_sp$sites,
    S_mitja = mat_sp$richness,
    S_sd    = mat_sp$sd
  )
}

# Separem AMP i NP
mat_AMP <- mat_div %>%
  filter(proteccio == "AMP") %>%
  select(-proteccio, -estacio, -transsecte) %>%
  as.matrix()

mat_NP <- mat_div %>%
  filter(proteccio == "NP") %>%
  select(-proteccio, -estacio, -transsecte) %>%
  as.matrix()

acum_AMP <- calcula_acumulacio(mat_AMP) %>% mutate(proteccio = "AMP")
acum_NP  <- calcula_acumulacio(mat_NP)  %>% mutate(proteccio = "NP")
acum_tot <- bind_rows(acum_AMP, acum_NP)

p_acum <- ggplot(acum_tot, aes(x = n_trans, y = S_mitja,
                                color = proteccio, fill = proteccio)) +
  geom_ribbon(aes(ymin = S_mitja - S_sd, ymax = S_mitja + S_sd),
              alpha = 0.2, color = NA) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2.5) +
  scale_color_manual(values = colors_prot,
                     labels = c("AMP" = "Àrea Marina Protegida", "NP" = "No Protegit")) +
  scale_fill_manual(values  = colors_prot,
                    labels = c("AMP" = "Àrea Marina Protegida", "NP" = "No Protegit")) +
  labs(title    = "Corba d'acumulació d'espècies (rarefacció)",
       subtitle = "Banda = ±1 SD de 999 aleatoritzacions",
       x = "Nombre de transsectes", y = "Nombre d'espècies (S)",
       color = "Protecció", fill = "Protecció") +
  theme_bw(base_size = 13) + theme(legend.position = "bottom")

print(p_acum)

Mostra/amaga el codi
desa_grafic("03_acumulacio_especies.png", p_acum, ample = 9, alt = 6)
Mostra/amaga el codi
# Estimem l'asímptota de Chao1 per valorar la completesa del mostreig
cat("=== Completesa del mostreig (estimador Chao1) ===\n\n")
=== Completesa del mostreig (estimador Chao1) ===
Mostra/amaga el codi
for (prot in c("AMP", "NP")) {
  mat_p <- mat_div %>%
    filter(proteccio == prot) %>%
    select(-proteccio, -estacio, -transsecte) %>%
    as.matrix()
  
  S_obs   <- ncol(mat_p[, colSums(mat_p) > 0])
  ch1     <- tryCatch(estimateR(colSums(mat_p))["S.chao1"], error = \(e) NA)
  complet <- round(S_obs / ch1 * 100, 1)
  cat(sprintf("  %s: S_obs = %d | Chao1 = %.1f | Completesa = %.1f%%\n",
              prot, S_obs, ch1, complet))
}
  AMP: S_obs = 21 | Chao1 = 22.0 | Completesa = 95.5%
  NP: S_obs = 17 | Chao1 = 18.0 | Completesa = 94.4%

5 Corba rang-abundància

Diagrama de Whittaker

Cada espècie és un punt. L’eix X ordena les espècies de més a menys abundants (rang). L’eix Y (escala logarítmica) mostra l’abundància relativa. Una corba molt pendent → poca equitativitat (unes poques espècies dominen). Una corba plana → comunitat equilibrada.

Mostra/amaga el codi
rang_df <- dades %>%
  group_by(proteccio, especie) %>%
  summarise(N_total = sum(nombre, na.rm = TRUE), .groups = "drop") %>%
  group_by(proteccio) %>%
  mutate(
    N_grup    = sum(N_total),
    abund_rel = N_total / N_grup * 100,
    rang      = rank(-N_total, ties.method = "first")
  ) %>%
  ungroup() %>%
  arrange(proteccio, rang)

# Top 5 per etiquetar
top5 <- rang_df %>% group_by(proteccio) %>% slice_min(rang, n = 5) %>% ungroup()

p_rang <- ggplot(rang_df, aes(x = rang, y = abund_rel,
                               color = proteccio, shape = proteccio)) +
  geom_line(linewidth = 0.9, alpha = 0.8) +
  geom_point(size = 2.5, alpha = 0.9) +
  geom_text_repel(data = top5, aes(label = especie),
                  size = 3.2, fontface = "italic", max.overlaps = 20, nudge_y = 0.3) +
  scale_y_log10(labels = label_number(suffix = "%")) +
  scale_color_manual(values = colors_prot,
                     labels = c("AMP" = "Àrea Marina Protegida", "NP" = "No Protegit")) +
  scale_shape_manual(values = c("AMP" = 16, "NP" = 17),
                     labels = c("AMP" = "Àrea Marina Protegida", "NP" = "No Protegit")) +
  labs(title    = "Diagrama de rang-abundància (Whittaker plot)",
       subtitle = "Escala logarítmica. S'etiqueten les 5 espècies més abundants.",
       x = "Rang", y = "Abundància relativa (%)",
       color = "Protecció", shape = "Protecció") +
  theme_bw(base_size = 13) + theme(legend.position = "bottom")

print(p_rang)

Mostra/amaga el codi
desa_grafic("04_rang_abundancia.png", p_rang, ample = 10, alt = 6)
Mostra/amaga el codi
rang_df %>%
  group_by(proteccio) %>%
  slice_min(rang, n = 10) %>%
  select(Protecció = proteccio, Rang = rang, Espècie = especie,
         N_total, `Abund.rel.(%)` = abund_rel) %>%
  mutate(`Abund.rel.(%)` = round(`Abund.rel.(%)`, 2)) %>%
  kable(caption = "Top 10 espècies més abundants per grup de protecció") %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Top 10 espècies més abundants per grup de protecció
Protecció Rang Espècie N_total Abund.rel.(%)
AMP 1 Sarpa salpa 3840 39.49
AMP 2 Chromis chromis 1847 18.99
AMP 3 Diplodus vulgaris 982 10.10
AMP 4 Coris julis 867 8.92
AMP 5 Diplodus sargus 594 6.11
AMP 6 Symphodus tinca 588 6.05
AMP 7 Thalassoma pavo 216 2.22
AMP 8 Serranus cabrilla 214 2.20
AMP 9 Chelon labrosus 168 1.73
AMP 10 Serranus scriba 129 1.33
NP 1 Sarpa salpa 1449 40.68
NP 2 Chromis chromis 850 23.86
NP 3 Diplodus vulgaris 361 10.13
NP 4 Diplodus sargus 180 5.05
NP 5 Thalassoma pavo 135 3.79
NP 6 Coris julis 134 3.76
NP 7 Symphodus tinca 116 3.26
NP 8 Boops boops 101 2.84
NP 9 Chelon labrosus 100 2.81
NP 10 Serranus cabrilla 56 1.57

6 Abundància (densitat)

6.1 Totes les espècies: AMP i NP (gràfics separats)

Consell

Les barres mostren la densitat mitjana per espècie. Les barres d’error representen ±1 SE (error estàndard).

Mostra/amaga el codi
abund_trans <- dades %>%
  group_by(proteccio, estacio, transsecte, especie) %>%
  summarise(N = sum(nombre, na.rm = TRUE), .groups = "drop") %>%
  mutate(densitat = N / 250)

abund_resum <- abund_trans %>%
  group_by(proteccio, especie) %>%
  summarise(
    mitjana = mean(densitat, na.rm = TRUE),
    se      = sd(densitat, na.rm = TRUE) / sqrt(n()),
    .groups = "drop"
  )

ord_esp <- abund_resum %>%
  group_by(especie) %>%
  summarise(mg = mean(mitjana)) %>%
  arrange(desc(mg)) %>%
  pull(especie)

abund_resum <- abund_resum %>%
  mutate(especie = factor(especie, levels = ord_esp))

# Funció per fer el gràfic per un grup
grafic_abund <- function(prot, color_fill) {
  abund_resum %>%
    filter(proteccio == prot) %>%
    ggplot(aes(x = especie, y = mitjana)) +
    geom_col(fill = color_fill, alpha = 0.85) +
    geom_errorbar(aes(ymin = mitjana - se, ymax = mitjana + se), width = 0.4) +
    labs(title    = paste("Densitat mitjana (±SE) per espècie –", prot),
         subtitle = "Tècnica: Comptatge visual",
         x = NULL, y = "Densitat (ind/250m²)") +
    theme_bw(base_size = 11) +
    theme(axis.text.x        = element_text(angle = 45, hjust = 1, face = "italic"),
          panel.grid.major.x = element_blank())
}

p_abund_AMP <- grafic_abund("AMP", "#FF7043")
p_abund_NP  <- grafic_abund("NP",  "#2196F3")

print(p_abund_AMP)

Mostra/amaga el codi
print(p_abund_NP)

Mostra/amaga el codi
desa_grafic("05a_densitat_global_AMP.png", p_abund_AMP, ample = 12, alt = 6)
desa_grafic("05b_densitat_global_NP.png",  p_abund_NP,  ample = 12, alt = 6)

6.2 Per espècie individual (AMP vs NP en el mateix gràfic)

Consell

Selecciona les espècies que t’interessin canviant la llista especies_sel al bloc de codi. Per defecte, es mostra el conjunt d’espècies amb densitat > 0 en algun grup.

Mostra/amaga el codi
# Llistat d'espècies presents
especies_all <- sort(unique(abund_trans$especie))

# Gràfic per a cada espècie
plots_abund_sp <- map(especies_all, function(sp) {
  df  <- abund_trans %>% filter(especie == sp)
  kw  <- tryCatch(kruskal.test(densitat ~ proteccio, data = df), error = \(e) NULL)
  sub <- if (!is.null(kw) && !is.na(kw$p.value))
    paste0("KW: χ²=", round(kw$statistic, 2), ", p=", round(kw$p.value, 4),
           " ", sig_label(kw$p.value))
  else "Test no disponible"

  df %>%
    group_by(proteccio) %>%
    summarise(m = mean(densitat, na.rm = TRUE),
              se = sd(densitat, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
    ggplot(aes(x = proteccio, y = m, fill = proteccio)) +
    geom_col(alpha = 0.85, width = 0.5) +
    geom_errorbar(aes(ymin = m - se, ymax = m + se), width = 0.15, linewidth = 0.8) +
    scale_fill_manual(values = colors_prot) +
    labs(title = sp, subtitle = sub, x = NULL, y = "Densitat (ind/250m²)") +
    theme_bw(base_size = 11) + theme(legend.position = "none",
                                      plot.title = element_text(face = "italic", size = 11))
})
names(plots_abund_sp) <- especies_all

# Mostrem tots els gràfics i els desem
for (sp in especies_all) {
  print(plots_abund_sp[[sp]])
  nom <- paste0("05c_densitat_", gsub(" ", "_", sp), ".png")
  desa_grafic(nom, plots_abund_sp[[sp]], ample = 5, alt = 4)
}

6.3 Anàlisi Kruskal-Wallis per espècie

Nota

El test de Kruskal-Wallis és l’equivalent no paramètric de l’ANOVA. Compara les distribucions de densitat entre AMP i NP. Un p-valor < 0.05 indica diferències significatives.

Mostra/amaga el codi
kw_abund <- map_dfr(especies_all, function(sp) {
  df <- abund_trans %>% filter(especie == sp)
  kw <- tryCatch(kruskal.test(densitat ~ proteccio, data = df), error = \(e) NULL)
  if (is.null(kw)) return(NULL)
  m    <- df %>% group_by(proteccio) %>%
          summarise(m = mean(densitat, na.rm = TRUE), .groups = "drop")
  mAMP <- m %>% filter(proteccio == "AMP") %>% pull(m)
  mNP  <- m %>% filter(proteccio == "NP")  %>% pull(m)
  tibble(
    Espècie   = sp,
    `χ²`      = round(kw$statistic, 3),
    df        = kw$parameter,
    p_valor   = round(kw$p.value, 4),
    Sig       = sig_label(kw$p.value),
    Direcció  = ifelse(kw$p.value < 0.05,
                       ifelse(length(mAMP) > 0 & length(mNP) > 0,
                              ifelse(mAMP > mNP, "AMP > NP", "AMP < NP"), ""), "")
  )
}) %>% arrange(p_valor)

kw_abund %>%
  kable(caption = "Kruskal-Wallis: densitat AMP vs NP per espècie") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
  row_spec(which(kw_abund$Sig != "ns"), background = "#BBDEFB")
Kruskal-Wallis: densitat AMP vs NP per espècie
Espècie χ² df p_valor Sig Direcció
Coris julis 19.105 1 0.0000 *** AMP > NP
Diplodus sargus 6.962 1 0.0083 ** AMP > NP
Diplodus puntazzo 3.341 1 0.0676 ns
Chromis chromis 3.317 1 0.0686 ns
Symphodus tinca 3.163 1 0.0753 ns
Epinephelus marginatus 1.870 1 0.1715 ns
Diplodus vulgaris 1.259 1 0.2619 ns
Serranus cabrilla 1.080 1 0.2987 ns
Mullus surmuletus 0.625 1 0.4292 ns
Serranus scriba 0.581 1 0.4459 ns
Sarpa salpa 0.544 1 0.4608 ns
Spondyliosoma cantharus 0.250 1 0.6171 ns
Sparus aurata 0.235 1 0.6280 ns
Chelon labrosus 0.153 1 0.6959 ns
Thalassoma pavo 0.121 1 0.7280 ns

7 Estructura de talles

Classes de talla
  • P = Petits (talla < 1/3 de la talla màxima de l’espècie)
  • M = Mitjans (talla entre 1/3 i 2/3)
  • G = Grans (talla > 2/3)

La distribució de talles ens informa sobre l’estructura d’edats de la població i l’efecte de la pesca. En zones protegides esperem trobar més individus grans.

Mostra/amaga el codi
# Preparem les dades de talles
talles_base <- dades %>%
  filter(!is.na(talla)) %>%
  group_by(proteccio, transsecte, especie, talla) %>%
  summarise(N = sum(nombre, na.rm = TRUE), .groups = "drop")

# Completem amb zeros explícits
# SOLUCIÓ: complete() sense group_by previ, especificant totes les combinacions
talles_completa <- talles_base %>%
  ungroup() %>%
  complete(
    nesting(proteccio, transsecte, especie),
    talla,
    fill = list(N = 0)
  )

# Comprovació
cat("Files talles_base:    ", nrow(talles_base), "\n")
Files talles_base:     1504 
Mostra/amaga el codi
cat("Files talles_completa:", nrow(talles_completa), "\n")
Files talles_completa: 2895 
Mostra/amaga el codi
# Percentatge i densitat per transsecte
talles_resum <- talles_completa %>%
  group_by(proteccio, transsecte, especie) %>%
  mutate(
    total    = sum(N),
    pct      = ifelse(total > 0, N / total * 100, 0),  # evita divisió per 0
    densitat = N / 250
  ) %>%
  ungroup() %>%
  group_by(proteccio, especie, talla) %>%
  summarise(
    pct_mitja      = mean(pct,      na.rm = TRUE),
    densitat_mitja = mean(densitat, na.rm = TRUE),
    se_dens        = sd(densitat,   na.rm = TRUE) / sqrt(n()),
    .groups        = "drop"
  )

7.1 % per classe de talla per espècie

Mostra/amaga el codi
especies_talles <- sort(unique(talles_resum$especie))

for (sp in especies_talles) {
  df <- talles_resum %>% filter(especie == sp)
  if (nrow(df) == 0) next
  
  p <- ggplot(df, aes(x = talla, y = pct_mitja, fill = proteccio)) +
    geom_col(position = position_dodge(width = 0.9, preserve = "single"), alpha = 0.85) +
    scale_fill_manual(values = colors_prot) +
    labs(title    = paste(sp, "– % per classe de talla"),
         subtitle = "Tècnica: Comptatge visual",
         x = "Classe de talla", y = "% individus", fill = "Protecció") +
    theme_bw(base_size = 13)
  
  print(p)
  desa_grafic(paste0("06a_talles_pct_", gsub(" ", "_", sp), ".png"), p, ample = 6, alt = 4)
}

7.2 Densitat per classe de talla per espècie

Mostra/amaga el codi
for (sp in especies_talles) {
  df <- talles_resum %>% filter(especie == sp)
  if (nrow(df) == 0) next
  
  p <- ggplot(df, aes(x = talla, y = densitat_mitja, fill = proteccio)) +
    geom_col(position = position_dodge(width = 0.9, preserve = "single"), alpha = 0.85) +
    geom_errorbar(aes(ymin = densitat_mitja - se_dens, ymax = densitat_mitja + se_dens),
                  position = position_dodge(width = 0.9, preserve = "single"), width = 0.25) +
    scale_fill_manual(values = colors_prot) +
    labs(title    = paste(sp, "– Densitat per classe de talla"),
         subtitle = "Tècnica: Comptatge visual (±SE)",
         x = "Classe de talla", y = "Densitat (ind/250m²)", fill = "Protecció") +
    theme_bw(base_size = 13)
  
  print(p)
  desa_grafic(paste0("06b_talles_dens_", gsub(" ", "_", sp), ".png"), p, ample = 6, alt = 4)
}


8 Biomassa

8.1 Totes les espècies: AMP i NP (gràfics separats)

Mostra/amaga el codi
bio_trans <- dades %>%
  group_by(proteccio, estacio, transsecte, especie) %>%
  summarise(B = sum(biomassa, na.rm = TRUE) / 250, .groups = "drop")

bio_resum <- bio_trans %>%
  group_by(proteccio, especie) %>%
  summarise(
    mitjana = mean(B, na.rm = TRUE),
    se      = sd(B, na.rm = TRUE) / sqrt(n()),
    .groups = "drop"
  )

ord_bio <- bio_resum %>%
  group_by(especie) %>%
  summarise(mg = mean(mitjana)) %>%
  arrange(desc(mg)) %>%
  pull(especie)

bio_resum <- bio_resum %>% mutate(especie = factor(especie, levels = ord_bio))

grafic_bio <- function(prot, color_fill) {
  bio_resum %>%
    filter(proteccio == prot) %>%
    ggplot(aes(x = especie, y = mitjana)) +
    geom_col(fill = color_fill, alpha = 0.85) +
    geom_errorbar(aes(ymin = mitjana - se, ymax = mitjana + se), width = 0.4) +
    labs(title = paste("Biomassa mitjana (±SE) –", prot), x = NULL, y = "Biomassa (g/250m²)") +
    theme_bw(base_size = 11) +
    theme(axis.text.x = element_text(angle = 45, hjust = 1, face = "italic"),
          panel.grid.major.x = element_blank())
}

p_bio_AMP <- grafic_bio("AMP", "#FF7043")
p_bio_NP  <- grafic_bio("NP",  "#2196F3")

print(p_bio_AMP)

Mostra/amaga el codi
print(p_bio_NP)

Mostra/amaga el codi
desa_grafic("07a_biomassa_global_AMP.png", p_bio_AMP, ample = 12, alt = 6)
desa_grafic("07b_biomassa_global_NP.png",  p_bio_NP,  ample = 12, alt = 6)

8.2 Per espècie individual

Mostra/amaga el codi
plots_bio_sp <- map(especies_all, function(sp) {
  df  <- bio_trans %>% filter(especie == sp)
  kw  <- tryCatch(kruskal.test(B ~ proteccio, data = df), error = \(e) NULL)
  sub <- if (!is.null(kw) && !is.na(kw$p.value))
    paste0("KW: χ²=", round(kw$statistic, 2), ", p=", round(kw$p.value, 4),
           " ", sig_label(kw$p.value))
  else "Test no disponible"

  df %>%
    group_by(proteccio) %>%
    summarise(m = mean(B, na.rm = TRUE), se = sd(B, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
    ggplot(aes(x = proteccio, y = m, fill = proteccio)) +
    geom_col(alpha = 0.85, width = 0.5) +
    geom_errorbar(aes(ymin = m - se, ymax = m + se), width = 0.15, linewidth = 0.8) +
    scale_fill_manual(values = colors_prot) +
    labs(title = sp, subtitle = sub, x = NULL, y = "Biomassa (g/250m²)") +
    theme_bw(base_size = 11) + theme(legend.position = "none",
                                      plot.title = element_text(face = "italic", size = 11))
})
names(plots_bio_sp) <- especies_all

for (sp in especies_all) {
  print(plots_bio_sp[[sp]])
  nom <- paste0("07c_biomassa_", gsub(" ", "_", sp), ".png")
  desa_grafic(nom, plots_bio_sp[[sp]], ample = 5, alt = 4)
}

8.3 Kruskal-Wallis per espècie (biomassa)

Mostra/amaga el codi
kw_bio <- map_dfr(especies_all, function(sp) {
  df <- bio_trans %>% filter(especie == sp)
  kw <- tryCatch(kruskal.test(B ~ proteccio, data = df), error = \(e) NULL)
  if (is.null(kw)) return(NULL)
  m    <- df %>% group_by(proteccio) %>%
          summarise(m = mean(B, na.rm = TRUE), .groups = "drop")
  mAMP <- m %>% filter(proteccio == "AMP") %>% pull(m)
  mNP  <- m %>% filter(proteccio == "NP")  %>% pull(m)
  tibble(
    Espècie  = sp,
    `χ²`     = round(kw$statistic, 3),
    df       = kw$parameter,
    p_valor  = round(kw$p.value, 4),
    Sig      = sig_label(kw$p.value),
    Direcció = ifelse(kw$p.value < 0.05,
                      ifelse(length(mAMP) > 0 & length(mNP) > 0,
                             ifelse(mAMP > mNP, "AMP > NP", "AMP < NP"), ""), "")
  )
}) %>% arrange(p_valor)

kw_bio %>%
  kable(caption = "Kruskal-Wallis: biomassa AMP vs NP per espècie") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
  row_spec(which(kw_bio$Sig != "ns"), background = "#BBDEFB")
Kruskal-Wallis: biomassa AMP vs NP per espècie
Espècie χ² df p_valor Sig Direcció
Diplodus sargus 17.371 1 0.0000 *** AMP > NP
Diplodus vulgaris 21.773 1 0.0000 *** AMP > NP
Coris julis 16.009 1 0.0001 *** AMP > NP
Thalassoma pavo 12.060 1 0.0005 *** AMP > NP
Symphodus tinca 7.622 1 0.0058 ** AMP > NP
Serranus cabrilla 7.427 1 0.0064 ** AMP > NP
Sarpa salpa 6.952 1 0.0084 ** AMP > NP
Epinephelus marginatus 4.516 1 0.0336 * AMP > NP
Serranus scriba 4.036 1 0.0445 * AMP > NP
Chelon labrosus 3.818 1 0.0507 ns
Sparus aurata 3.363 1 0.0667 ns
Diplodus puntazzo 2.539 1 0.1110 ns
Spondyliosoma cantharus 0.500 1 0.4795 ns
Chromis chromis 0.269 1 0.6038 ns
Mullus surmuletus 0.000 1 1.0000 ns

9 Anàlisi multivariant: PCA i RDA

Mètodes multivariants
  • PCA (Anàlisi de Components Principals): redueix la dimensionalitat de la matriu d’espècies (biomassa per estació). Estacions pròximes al biplot comparteixen composició similar. S’aplica la transformació de Hellinger abans del PCA.
  • RDA (Anàlisi de Redundància): combina la matriu d’espècies amb variables ambientals (profunditat, rugositat, cobertures). Permet identificar quins factors ambientals expliquen les diferències entre comunitats.

9.1 PCA – Composició per espècie i estació

Mostra/amaga el codi
# Matriu estació × espècie (biomassa total)
mat_est <- dades %>%
  group_by(estacio, proteccio, especie) %>%
  summarise(B = sum(biomassa, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = especie, values_from = B, values_fill = 0)

meta_est <- mat_est %>% select(estacio, proteccio)
mat_sp   <- mat_est %>% select(-estacio, -proteccio) %>% as.matrix()
rownames(mat_sp) <- meta_est$estacio

# Transformació Hellinger (recomanada per a dades d'abundància)
mat_hell <- decostand(mat_sp, method = "hellinger")

# PCA
pca_res  <- rda(mat_hell)
eig      <- eigenvals(pca_res)
var_exp  <- round(eig / sum(eig) * 100, 1)

cat(sprintf("PC1: %.1f%%  |  PC2: %.1f%%  |  PC3: %.1f%%\n",
            var_exp[1], var_exp[2], var_exp[3]))
PC1: 61.4%  |  PC2: 17.5%  |  PC3: 15.0%
Mostra/amaga el codi
# Scores
sites_pca   <- as.data.frame(scores(pca_res, display = "sites",   scaling = 2))
species_pca <- as.data.frame(scores(pca_res, display = "species", scaling = 2))
sites_pca$estacio   <- rownames(sites_pca)
sites_pca$proteccio <- meta_est$proteccio
species_pca$especie <- rownames(species_pca)

# Escala les fletxes de les espècies
sf <- max(abs(sites_pca[, 1:2])) / max(abs(species_pca[, 1:2])) * 0.6
species_pca[, 1:2] <- species_pca[, 1:2] * sf

p_pca <- ggplot() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "grey70") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "grey70") +
  geom_point(data = sites_pca, aes(PC1, PC2, color = proteccio, shape = proteccio), size = 3.5) +
  geom_text_repel(data = sites_pca, aes(PC1, PC2, label = estacio, color = proteccio), size = 3.2) +
  geom_segment(data = species_pca, aes(0, 0, xend = PC1, yend = PC2),
               arrow = arrow(length = unit(0.2, "cm")), color = "grey40", linewidth = 0.5) +
  geom_text_repel(data = species_pca, aes(PC1, PC2, label = especie),
                  color = "grey30", size = 2.8, fontface = "italic", max.overlaps = 15) +
  scale_color_manual(values = colors_prot) +
  scale_shape_manual(values = c("AMP" = 16, "NP" = 17)) +
  labs(title = "PCA – Biomassa per espècie i estació (transformació Hellinger)",
       x     = paste0("PC1 (", var_exp[1], "%)"),
       y     = paste0("PC2 (", var_exp[2], "%)"),
       color = "Protecció", shape = "Protecció") +
  theme_bw(base_size = 12) + theme(legend.position = "bottom")

print(p_pca)

Mostra/amaga el codi
desa_grafic("08a_PCA.png", p_pca, ample = 10, alt = 8)

9.2 RDA – Efecte de les variables ambientals

Mostra/amaga el codi
# Variables ambientals per estació (medianes)
vars_amb <- dades %>%
  distinct(estacio, transsecte, prof, pendent, rugositat,
           megablocs, blocs_mitjans, blocs_petits,
           roca_base, coraligen, pedres, sorra, posidonia) %>%
  mutate(across(prof:posidonia, \(x) as.numeric(gsub(",", ".", x)))) %>%
  group_by(estacio) %>%
  summarise(across(prof:posidonia, \(x) median(x, na.rm = TRUE)), .groups = "drop") %>%
  filter(estacio %in% meta_est$estacio) %>%
  arrange(match(estacio, meta_est$estacio))

env_mat <- vars_amb %>% select(-estacio) %>% as.matrix()
rownames(env_mat) <- vars_amb$estacio
# Eliminem variables amb NA o variància 0
env_mat <- env_mat[, apply(env_mat, 2, \(x) !any(is.na(x)) && var(x) > 0)]

rda_res <- tryCatch(rda(mat_hell ~ ., data = as.data.frame(env_mat)), error = \(e) NULL)

if (!is.null(rda_res)) {
  rda_eig <- eigenvals(rda_res, model = "constrained")
  rda_var <- round(rda_eig / sum(eigenvals(rda_res)) * 100, 1)

  sites_r   <- as.data.frame(scores(rda_res, display = "sites",   scaling = 2))
  species_r <- as.data.frame(scores(rda_res, display = "species", scaling = 2))
  bp_r      <- as.data.frame(scores(rda_res, display = "bp",      scaling = 2))
  sites_r$estacio   <- rownames(sites_r)
  sites_r$proteccio <- meta_est$proteccio
  species_r$especie <- rownames(species_r)
  bp_r$variable     <- rownames(bp_r)

  sf_sp  <- max(abs(sites_r[, 1:2])) / max(abs(species_r[, 1:2])) * 0.55
  sf_env <- max(abs(sites_r[, 1:2])) / max(abs(bp_r[, 1:2]))      * 0.70
  species_r[, 1:2] <- species_r[, 1:2] * sf_sp
  bp_r[, 1:2]      <- bp_r[, 1:2]      * sf_env

  p_rda <- ggplot() +
    geom_hline(yintercept = 0, linetype = "dashed", color = "grey70") +
    geom_vline(xintercept = 0, linetype = "dashed", color = "grey70") +
    geom_point(data = sites_r,   aes(RDA1, RDA2, color = proteccio, shape = proteccio), size = 3.5) +
    geom_text_repel(data = sites_r,   aes(RDA1, RDA2, label = estacio, color = proteccio), size = 3) +
    geom_segment(data = species_r, aes(0, 0, xend = RDA1, yend = RDA2),
                 arrow = arrow(length = unit(0.2, "cm")), color = "#388E3C", linewidth = 0.5) +
    geom_text_repel(data = species_r, aes(RDA1, RDA2, label = especie),
                    color = "#1B5E20", size = 2.5, fontface = "italic", max.overlaps = 15) +
    geom_segment(data = bp_r, aes(0, 0, xend = RDA1, yend = RDA2),
                 arrow = arrow(length = unit(0.25, "cm")), color = "#E65100", linewidth = 0.8) +
    geom_text_repel(data = bp_r, aes(RDA1, RDA2, label = variable),
                    color = "#BF360C", size = 3.2, fontface = "bold", max.overlaps = 15) +
    scale_color_manual(values = colors_prot) +
    scale_shape_manual(values = c("AMP" = 16, "NP" = 17)) +
    labs(title    = "RDA – Biomassa vs. variables ambientals",
         subtitle = paste0("RDA1: ", rda_var[1], "%  |  RDA2: ", rda_var[2], "%"),
         x        = paste0("RDA1 (", rda_var[1], "%)"),
         y        = paste0("RDA2 (", rda_var[2], "%)"),
         color = "Protecció", shape = "Protecció") +
    theme_bw(base_size = 12) + theme(legend.position = "bottom")

  print(p_rda)
  desa_grafic("08b_RDA.png", p_rda, ample = 11, alt = 9)

  cat("\n=== Test de permutació (ANOVA sobre RDA) ===\n")
  print(anova(rda_res, permutations = 999))
} else {
  cat("RDA no disponible (possiblement per manca de dades ambientals).\n")
}


=== Test de permutació (ANOVA sobre RDA) ===
No residual component

Model: rda(formula = mat_hell ~ prof + pendent + rugositat + megablocs + blocs_mitjans + blocs_petits + roca_base + sorra + posidonia, data = as.data.frame(env_mat))
         Df Variance F Pr(>F)
Model     4  0.15101         
Residual  0  0.00000         

10 Resum per espècie

Aquesta secció mostra de forma integrada tots els indicadors per a cada espècie.

Mostra/amaga el codi
for (sp in especies_all) {
  cat(sprintf("\n## *%s* {.unnumbered}\n\n", sp))

  # Taula de resum
  resum_sp <- bind_rows(
    abund_trans %>% filter(especie == sp) %>%
      group_by(proteccio) %>%
      summarise(variable = "Densitat (ind/250m²)",
                mitja = round(mean(densitat, na.rm = TRUE), 4),
                sd    = round(sd(densitat,   na.rm = TRUE), 4),
                .groups = "drop"),
    bio_trans %>% filter(especie == sp) %>%
      group_by(proteccio) %>%
      summarise(variable = "Biomassa (g/250m²)",
                mitja = round(mean(B, na.rm = TRUE), 3),
                sd    = round(sd(B,   na.rm = TRUE), 3),
                .groups = "drop")
  )

  print(
    kable(resum_sp, caption = paste("Resum:", sp),
          col.names = c("Protecció", "Variable", "Mitja", "SD")) %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                    full_width = FALSE)
  )

  # Gràfics abundància + biomassa costat a costat
  p_ab <- plots_abund_sp[[sp]]
  p_bi <- plots_bio_sp[[sp]]

  if (!is.null(p_ab) && !is.null(p_bi)) {
    print(p_ab + p_bi)
    combo <- p_ab + p_bi
    desa_grafic(paste0("09_resum_", gsub(" ", "_", sp), ".png"), combo, ample = 10, alt = 4)
  }

  # Talles si existeixen
  df_t <- talles_resum %>% filter(especie == sp)
  if (nrow(df_t) > 0) {
    p_t <- ggplot(df_t, aes(x = talla, y = pct_mitja, fill = proteccio)) +
      geom_col(position = position_dodge(width = 0.9), alpha = 0.85) +
      scale_fill_manual(values = colors_prot) +
      labs(title = paste(sp, "– % per talla"), x = NULL, y = "%", fill = NULL) +
      theme_bw(base_size = 11) + theme(legend.position = "bottom")
    print(p_t)
  }

  cat("\n---\n")
}

Boops boops

Resum: Boops boops
Protecció Variable Mitja SD
NP Densitat (ind/250m²) 0.101 0.0817
NP Biomassa (g/250m²) 0.252 0.2040


Chelon labrosus

Resum: Chelon labrosus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.1344 0.159
NP Densitat (ind/250m²) 0.2000 0.000
AMP Biomassa (g/250m²) 21.2220 16.897
NP Biomassa (g/250m²) 2.8010 0.000

10.1

Chromis chromis

Resum: Chromis chromis
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.2548 0.3190
NP Densitat (ind/250m²) 0.1214 0.0985
AMP Biomassa (g/250m²) 0.7790 1.0920
NP Biomassa (g/250m²) 1.0250 2.8830


Coris julis

Resum: Coris julis
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0439 0.0374
NP Densitat (ind/250m²) 0.0185 0.0193
AMP Biomassa (g/250m²) 2.0640 2.6520
NP Biomassa (g/250m²) 0.5900 1.0570

10.2

Dentex dentex

Resum: Dentex dentex
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0048 0.0016
AMP Biomassa (g/250m²) 5.5100 3.0470


Dicentrarchus labrax

Resum: Dicentrarchus labrax
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0048 0.0017
AMP Biomassa (g/250m²) 2.6560 1.9410

10.3

Diplodus annularis

Resum: Diplodus annularis
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.004 NA
AMP Biomassa (g/250m²) 0.155 NA


Diplodus cervinus

Resum: Diplodus cervinus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.004 NA
AMP Biomassa (g/250m²) 1.959 NA

10.4

Diplodus puntazzo

Resum: Diplodus puntazzo
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0102 0.0079
NP Densitat (ind/250m²) 0.0060 0.0039
AMP Biomassa (g/250m²) 2.0580 1.8140
NP Biomassa (g/250m²) 1.0590 0.8080


Diplodus sargus

Resum: Diplodus sargus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0313 0.0310
NP Densitat (ind/250m²) 0.0257 0.0399
AMP Biomassa (g/250m²) 6.9350 8.8430
NP Biomassa (g/250m²) 1.7800 2.2530

10.5

Diplodus vulgaris

Resum: Diplodus vulgaris
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0462 0.0459
NP Densitat (ind/250m²) 0.0370 0.0299
AMP Biomassa (g/250m²) 7.8910 8.2110
NP Biomassa (g/250m²) 2.3450 2.2630


Epinephelus marginatus

Resum: Epinephelus marginatus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0051 0.002
NP Densitat (ind/250m²) 0.0040 0.000
AMP Biomassa (g/250m²) 15.0990 22.467
NP Biomassa (g/250m²) 0.3840 0.146

10.6

Labrus merula

Resum: Labrus merula
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0078 0.0047
AMP Biomassa (g/250m²) 1.7960 1.7450


Labrus viridis

Resum: Labrus viridis
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0046 0.0015
AMP Biomassa (g/250m²) 0.3550 0.2300

10.7

Mullus surmuletus

Resum: Mullus surmuletus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.007 0.0038
NP Densitat (ind/250m²) 0.004 NA
AMP Biomassa (g/250m²) 0.951 1.0290
NP Biomassa (g/250m²) 0.523 NA


Oblada melanura

Resum: Oblada melanura
Protecció Variable Mitja SD
NP Densitat (ind/250m²) 0.160 NA
NP Biomassa (g/250m²) 7.749 NA

10.8

Sarpa salpa

Resum: Sarpa salpa
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.2163 0.2786
NP Densitat (ind/250m²) 0.1999 0.2772
AMP Biomassa (g/250m²) 24.9550 29.1780
NP Biomassa (g/250m²) 19.9630 31.3830


Serranus cabrilla

Resum: Serranus cabrilla
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0162 0.0112
NP Densitat (ind/250m²) 0.0132 0.0097
AMP Biomassa (g/250m²) 0.5930 0.7810
NP Biomassa (g/250m²) 0.3110 0.7420

10.9

Serranus scriba

Resum: Serranus scriba
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0094 0.0066
NP Densitat (ind/250m²) 0.0070 0.0035
AMP Biomassa (g/250m²) 0.5180 0.6200
NP Biomassa (g/250m²) 0.1050 0.1380


Sparus aurata

Resum: Sparus aurata
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0079 0.0050
NP Densitat (ind/250m²) 0.0060 0.0028
AMP Biomassa (g/250m²) 5.3380 5.9000
NP Biomassa (g/250m²) 0.6830 0.8610

10.10

Spondyliosoma cantharus

Resum: Spondyliosoma cantharus
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.007 0.006
NP Densitat (ind/250m²) 0.004 NA
AMP Biomassa (g/250m²) 1.670 1.750
NP Biomassa (g/250m²) 0.211 NA


Symphodus tinca

Resum: Symphodus tinca
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0341 0.0395
NP Densitat (ind/250m²) 0.0172 0.0137
AMP Biomassa (g/250m²) 5.6300 11.9920
NP Biomassa (g/250m²) 1.8960 2.7870

10.11

Thalassoma pavo

Resum: Thalassoma pavo
Protecció Variable Mitja SD
AMP Densitat (ind/250m²) 0.0222 0.0180
NP Densitat (ind/250m²) 0.0245 0.0205
AMP Biomassa (g/250m²) 0.3350 0.3130
NP Biomassa (g/250m²) 0.1310 0.1770



11 Resum de gràfics desats

Mostra/amaga el codi
fitxers <- list.files("grafics_visual", pattern = "\\.png$", full.names = FALSE)
cat(sprintf("Total de gràfics desats a 'grafics_visual/': %d\n\n", length(fitxers)))
Total de gràfics desats a 'grafics_visual/': 131
Mostra/amaga el codi
for (f in sort(fitxers)) cat(sprintf("  • %s\n", f))
  • 01a_boxplot_densitat_AMP.png
  • 01b_boxplot_densitat_NP.png
  • 01c_boxplot_biomassa_AMP.png
  • 01d_boxplot_biomassa_NP.png
  • 02a_boxplot_riquesa_S.png
  • 02b_boxplot_shannon.png
  • 02c_boxplot_pielou.png
  • 02d_violin_diversitat.png
  • 03_acumulacio_especies.png
  • 04_rang_abundancia.png
  • 05a_densitat_global_AMP.png
  • 05b_densitat_global_NP.png
  • 05c_densitat_Boops_boops.png
  • 05c_densitat_Chelon_labrosus.png
  • 05c_densitat_Chromis_chromis.png
  • 05c_densitat_Coris_julis.png
  • 05c_densitat_Dentex_dentex.png
  • 05c_densitat_Dicentrarchus_labrax.png
  • 05c_densitat_Diplodus_annularis.png
  • 05c_densitat_Diplodus_cervinus.png
  • 05c_densitat_Diplodus_puntazzo.png
  • 05c_densitat_Diplodus_sargus.png
  • 05c_densitat_Diplodus_vulgaris.png
  • 05c_densitat_Epinephelus_marginatus.png
  • 05c_densitat_Labrus_merula.png
  • 05c_densitat_Labrus_viridis.png
  • 05c_densitat_Mullus_surmuletus.png
  • 05c_densitat_Oblada_melanura.png
  • 05c_densitat_Sarpa_salpa.png
  • 05c_densitat_Serranus_cabrilla.png
  • 05c_densitat_Serranus_scriba.png
  • 05c_densitat_Sparus_aurata.png
  • 05c_densitat_Spondyliosoma_cantharus.png
  • 05c_densitat_Symphodus_tinca.png
  • 05c_densitat_Thalassoma_pavo.png
  • 06a_talles_pct_Boops_boops.png
  • 06a_talles_pct_Chelon_labrosus.png
  • 06a_talles_pct_Chromis_chromis.png
  • 06a_talles_pct_Coris_julis.png
  • 06a_talles_pct_Dentex_dentex.png
  • 06a_talles_pct_Dicentrarchus_labrax.png
  • 06a_talles_pct_Diplodus_annularis.png
  • 06a_talles_pct_Diplodus_cervinus.png
  • 06a_talles_pct_Diplodus_puntazzo.png
  • 06a_talles_pct_Diplodus_sargus.png
  • 06a_talles_pct_Diplodus_vulgaris.png
  • 06a_talles_pct_Epinephelus_marginatus.png
  • 06a_talles_pct_Labrus_merula.png
  • 06a_talles_pct_Labrus_viridis.png
  • 06a_talles_pct_Mullus_surmuletus.png
  • 06a_talles_pct_Oblada_melanura.png
  • 06a_talles_pct_Sarpa_salpa.png
  • 06a_talles_pct_Serranus_cabrilla.png
  • 06a_talles_pct_Serranus_scriba.png
  • 06a_talles_pct_Sparus_aurata.png
  • 06a_talles_pct_Spondyliosoma_cantharus.png
  • 06a_talles_pct_Symphodus_tinca.png
  • 06a_talles_pct_Thalassoma_pavo.png
  • 06b_talles_dens_Boops_boops.png
  • 06b_talles_dens_Chelon_labrosus.png
  • 06b_talles_dens_Chromis_chromis.png
  • 06b_talles_dens_Coris_julis.png
  • 06b_talles_dens_Dentex_dentex.png
  • 06b_talles_dens_Dicentrarchus_labrax.png
  • 06b_talles_dens_Diplodus_annularis.png
  • 06b_talles_dens_Diplodus_cervinus.png
  • 06b_talles_dens_Diplodus_puntazzo.png
  • 06b_talles_dens_Diplodus_sargus.png
  • 06b_talles_dens_Diplodus_vulgaris.png
  • 06b_talles_dens_Epinephelus_marginatus.png
  • 06b_talles_dens_Labrus_merula.png
  • 06b_talles_dens_Labrus_viridis.png
  • 06b_talles_dens_Mullus_surmuletus.png
  • 06b_talles_dens_Oblada_melanura.png
  • 06b_talles_dens_Sarpa_salpa.png
  • 06b_talles_dens_Serranus_cabrilla.png
  • 06b_talles_dens_Serranus_scriba.png
  • 06b_talles_dens_Sparus_aurata.png
  • 06b_talles_dens_Spondyliosoma_cantharus.png
  • 06b_talles_dens_Symphodus_tinca.png
  • 06b_talles_dens_Thalassoma_pavo.png
  • 07a_biomassa_global_AMP.png
  • 07b_biomassa_global_NP.png
  • 07c_biomassa_Boops_boops.png
  • 07c_biomassa_Chelon_labrosus.png
  • 07c_biomassa_Chromis_chromis.png
  • 07c_biomassa_Coris_julis.png
  • 07c_biomassa_Dentex_dentex.png
  • 07c_biomassa_Dicentrarchus_labrax.png
  • 07c_biomassa_Diplodus_annularis.png
  • 07c_biomassa_Diplodus_cervinus.png
  • 07c_biomassa_Diplodus_puntazzo.png
  • 07c_biomassa_Diplodus_sargus.png
  • 07c_biomassa_Diplodus_vulgaris.png
  • 07c_biomassa_Epinephelus_marginatus.png
  • 07c_biomassa_Labrus_merula.png
  • 07c_biomassa_Labrus_viridis.png
  • 07c_biomassa_Mullus_surmuletus.png
  • 07c_biomassa_Oblada_melanura.png
  • 07c_biomassa_Sarpa_salpa.png
  • 07c_biomassa_Serranus_cabrilla.png
  • 07c_biomassa_Serranus_scriba.png
  • 07c_biomassa_Sparus_aurata.png
  • 07c_biomassa_Spondyliosoma_cantharus.png
  • 07c_biomassa_Symphodus_tinca.png
  • 07c_biomassa_Thalassoma_pavo.png
  • 08a_PCA.png
  • 08b_RDA.png
  • 09_resum_Boops_boops.png
  • 09_resum_Chelon_labrosus.png
  • 09_resum_Chromis_chromis.png
  • 09_resum_Coris_julis.png
  • 09_resum_Dentex_dentex.png
  • 09_resum_Dicentrarchus_labrax.png
  • 09_resum_Diplodus_annularis.png
  • 09_resum_Diplodus_cervinus.png
  • 09_resum_Diplodus_puntazzo.png
  • 09_resum_Diplodus_sargus.png
  • 09_resum_Diplodus_vulgaris.png
  • 09_resum_Epinephelus_marginatus.png
  • 09_resum_Labrus_merula.png
  • 09_resum_Labrus_viridis.png
  • 09_resum_Mullus_surmuletus.png
  • 09_resum_Oblada_melanura.png
  • 09_resum_Sarpa_salpa.png
  • 09_resum_Serranus_cabrilla.png
  • 09_resum_Serranus_scriba.png
  • 09_resum_Sparus_aurata.png
  • 09_resum_Spondyliosoma_cantharus.png
  • 09_resum_Symphodus_tinca.png
  • 09_resum_Thalassoma_pavo.png

11.1 Anàlisi completada

Tots els gràfics s’han desat a la carpeta grafics_visual/. Pots obrir-los directament des de l’explorador de fitxers.


Document generat amb Quarto. Grau de Ciències del Mar – Universitat de Barcelona, 2026.