Multidimensional Phenotypic Plasticity
Analysis with phenop
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.width = 7,
fig.height = 5,
fig.align = "center",
warning = FALSE,
message = FALSE,
out.width = "100%"
)
# Load required packages
library(phenop)
library(ggplot2)
library(dplyr)
library(tidyr)
library(patchwork)
if (requireNamespace("kableExtra", quietly = TRUE)) {
library(kableExtra)
} else {
message("kableExtra no está instalado, usando formato básico para tablas")
# Código alternativo sin kableExtra
kable <- function(x, ...) knitr::kable(x, ...)
}
# Set theme for consistent plotting
theme_set(theme_minimal(base_size = 12))
# Load all synthetic datasets
demo_data <- read.csv("../inst/extdata/demo_data_synthetic.csv")
pheno_params <- read.csv("../inst/extdata/pheno_parameters_synthetic.csv")
pheno_timeseries <- read.csv("../inst/extdata/pheno_time_series_synthetic.csv")
tree_insect_fungi <- read.csv("../inst/extdata/tree_insect_fungi_synthetic.csv")
# Create comprehensive data summary table
data_summary <- data.frame(
Dataset = c("demo_data", "pheno_params", "pheno_timeseries", "tree_insect_fungi"),
Rows = c(nrow(demo_data), nrow(pheno_params), nrow(pheno_timeseries), nrow(tree_insect_fungi)),
Columns = c(ncol(demo_data), ncol(pheno_params), ncol(pheno_timeseries), ncol(tree_insect_fungi)),
Key_Variables = c(
"tree_species, temperature, insect_size",
"species, population_id, los, ndvi_max",
"species, year, doy, ndvi",
"tree_species, fungi_strain, temperature"
)
)
kable(data_summary, caption = "Dataset Summary") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Dataset Summary
|
Dataset
|
Rows
|
Columns
|
Key_Variables
|
|
demo_data
|
200
|
9
|
tree_species, temperature, insect_size
|
|
pheno_params
|
900
|
15
|
species, population_id, los, ndvi_max
|
|
pheno_timeseries
|
20784
|
29
|
species, year, doy, ndvi
|
|
tree_insect_fungi
|
1000
|
25
|
tree_species, fungi_strain, temperature
|
# DIAGNÓSTICO INICIAL
cat("=== DIAGNÓSTICO DE DATOS ===\n\n")
#> === DIAGNÓSTICO DE DATOS ===
# Verificar datasets
datasets <- list(
demo_data = demo_data,
pheno_params = pheno_params,
pheno_timeseries = pheno_timeseries,
tree_insect_fungi = tree_insect_fungi
)
for (name in names(datasets)) {
cat(sprintf("\n%s: %d filas, %d columnas\n", name,
nrow(datasets[[name]]), ncol(datasets[[name]])))
cat("Columnas:", paste(names(datasets[[name]]), collapse = ", "), "\n")
cat("Primeras filas:\n")
print(head(datasets[[name]], 2))
}
#>
#> demo_data: 200 filas, 9 columnas
#> Columnas: tree_species, insect_population, fungus_strain, temperature, humidity, insect_size, insect_resistance, fungus_growth, fungus_virulence
#> Primeras filas:
#> tree_species insect_population fungus_strain temperature humidity insect_size
#> 1 Tree_C Insect_A Fungi_4 4.833561 42.93376 9.349075
#> 2 Tree_B Insect_E Fungi_4 12.520354 35.91140 13.634231
#> insect_resistance fungus_growth fungus_virulence
#> 1 0.7982085 10.333223 0.2884884
#> 2 0.6650612 3.603476 0.4126962
#>
#> pheno_params: 900 filas, 15 columnas
#> Columnas: species, population_id, site, year, sos, eos, los, ndvi_max, gpp_total, mean_temperature, total_precipitation, parameter_id, longitude, latitude, elevation
#> Primeras filas:
#> species population_id site year sos eos los ndvi_max gpp_total
#> 1 Quercus_ilex POP_001 SITE_0002 2018 81 285 204 0.8764245 525.4685
#> 2 Quercus_ilex POP_001 SITE_0012 2019 91 289 198 0.8646511 392.6249
#> mean_temperature total_precipitation parameter_id longitude latitude
#> 1 12.189758 619.3601 PAR_0000001 -6.777098 37.89499
#> 2 5.977057 870.9965 PAR_0000002 -4.906184 51.20393
#> elevation
#> 1 1918
#> 2 1362
#>
#> pheno_timeseries: 20784 filas, 29 columnas
#> Columnas: observation_id, population_id, species, site, year, date, doy, month, season, latitude, longitude, elevation, soil_type, soil_pH, slope, ndvi, evi, temperature, gpp, lai, precipitation, gdd, vegetation_health, sos_adj, eos_adj, budburst, flowering, senescence, quality_flag
#> Primeras filas:
#> observation_id population_id species site year date doy
#> 1 OBS_000001 POP_18 Quercus_ilex SITE_0001 2018 2018-01-01 1.000000
#> 2 OBS_000002 POP_18 Quercus_ilex SITE_0001 2018 2018-01-09 9.878049
#> month season latitude longitude elevation soil_type soil_pH slope
#> 1 1 winter 51.84463 15.10828 1037 Sandy 5.846169 6.568729
#> 2 1 winter 51.84463 15.10828 1037 Sandy 5.846169 6.568729
#> ndvi evi temperature gpp lai precipitation gdd
#> 1 0.1925215 0.1182962 8.255168 3.893330 1.525155 1.714373 3.255168
#> 2 0.2619556 0.1992781 9.054500 4.846828 1.207258 2.302410 7.309667
#> vegetation_health sos_adj eos_adj budburst flowering senescence quality_flag
#> 1 20.54308 0 0 0 0 0 excellent
#> 2 31.86416 0 0 0 0 0 excellent
#>
#> tree_insect_fungi: 1000 filas, 25 columnas
#> Columnas: tree_species, insect_species, fungi_strain, site, year, interaction_id, temperature, humidity, tree_dbh, tree_height, tree_health, insect_size_base, insect_size, insect_resistance_base, insect_resistance, fungus_growth_base, fungus_growth, fungus_virulence_base, fungus_virulence, tree_damage, infection_success, vector_efficiency, tree_defense_response, observation_date, observer_id
#> Primeras filas:
#> tree_species insect_species fungi_strain site year interaction_id
#> 1 Tree_E Insect_E Fungi_2 SITE_0012 2023 1
#> 2 Tree_A Insect_C Fungi_1 SITE_0015 2019 2
#> temperature humidity tree_dbh tree_height tree_health insect_size_base
#> 1 21.94894 95.00000 42.03238 31.73272 0.7106098 14
#> 2 11.14997 64.82364 21.17648 15.25425 0.5370428 18
#> insect_size insect_resistance_base insect_resistance fungus_growth_base
#> 1 17.23693 0.6 0.6113315 15
#> 2 16.07760 0.5 0.5382351 12
#> fungus_growth fungus_virulence_base fungus_virulence tree_damage
#> 1 30.50295 0.6 0.6161388 0.04624934
#> 2 13.73765 0.8 0.7890024 0.16781104
#> infection_success vector_efficiency tree_defense_response observation_date
#> 1 0 6.637828 1.0691792 2023-07-05
#> 2 0 6.978796 0.4953102 2019-05-03
#> observer_id
#> 1 OBSV_1819
#> 2 OBSV_1304
# Verificar funciones disponibles
cat("\n=== FUNCIONES DISPONIBLES EN phenop ===\n")
#>
#> === FUNCIONES DISPONIBLES EN phenop ===
funciones_phenop <- ls("package:phenop")
cat("Número de funciones:", length(funciones_phenop), "\n")
#> Número de funciones: 38
cat("Primeras 20 funciones:", paste(head(funciones_phenop, 20), collapse = ", "), "\n")
#> Primeras 20 funciones: %>%, analyze_correlations, analyze_gam_relationships, analyze_mixed_effects, analyze_temporal_trends, anova_plasticity, create_phenology_dashboard, datos_demo, demo_data, detect_anomalies, fungi_insect_data, host_pathogen_interaction, host_pathogen_interaction_extended, multidim_plasticity, optimize_multidim_environment, perform_cluster_analysis, perform_pca_analysis, pheno_parameters, pheno_spatial, pheno_spatial_sf
# Preview each dataset
cat("## Demo Data Preview\n")
#> ## Demo Data Preview
print(head(demo_data))
#> tree_species insect_population fungus_strain temperature humidity insect_size
#> 1 Tree_C Insect_A Fungi_4 4.8335610 42.93376 9.349075
#> 2 Tree_B Insect_E Fungi_4 12.5203540 35.91140 13.634231
#> 3 Tree_A Insect_C Fungi_1 1.5617753 30.00000 14.974024
#> 4 Tree_C Insect_C Fungi_4 8.2413980 70.82887 14.151851
#> 5 Tree_A Insect_D Fungi_3 -0.8268642 92.03394 6.094436
#> 6 Tree_C Insect_C Fungi_4 17.2928796 30.00000 20.820027
#> insect_resistance fungus_growth fungus_virulence
#> 1 0.7982085 10.333223 0.2884884
#> 2 0.6650612 3.603476 0.4126962
#> 3 0.5222675 1.030663 0.6378264
#> 4 0.6891663 23.537315 0.6622147
#> 5 0.7861821 17.387904 0.6174612
#> 6 0.3619049 4.129653 0.7698455
cat("\n## Phenological Parameters Preview\n")
#>
#> ## Phenological Parameters Preview
print(head(pheno_params))
#> species population_id site year sos eos los ndvi_max gpp_total
#> 1 Quercus_ilex POP_001 SITE_0002 2018 81 285 204 0.8764245 525.4685
#> 2 Quercus_ilex POP_001 SITE_0012 2019 91 289 198 0.8646511 392.6249
#> 3 Quercus_ilex POP_001 SITE_0007 2020 87 281 194 0.8207439 420.6072
#> 4 Quercus_ilex POP_001 SITE_0001 2021 87 295 208 0.8250511 486.4284
#> 5 Quercus_ilex POP_001 SITE_0009 2022 93 279 186 0.9037395 489.6368
#> 6 Quercus_ilex POP_001 SITE_0004 2023 87 278 191 0.7783195 535.3112
#> mean_temperature total_precipitation parameter_id longitude latitude
#> 1 12.189758 619.3601 PAR_0000001 -6.7770983 37.89499
#> 2 5.977057 870.9965 PAR_0000002 -4.9061837 51.20393
#> 3 8.823630 672.2411 PAR_0000003 -2.2385959 49.17922
#> 4 8.255076 699.2963 PAR_0000004 -3.8030810 39.50687
#> 5 10.883191 711.4369 PAR_0000005 -0.7664164 46.68026
#> 6 11.720395 780.7555 PAR_0000006 1.3233951 48.00547
#> elevation
#> 1 1918
#> 2 1362
#> 3 38
#> 4 1325
#> 5 1825
#> 6 852
cat("\n## Key Variable Distributions\n")
#>
#> ## Key Variable Distributions
summary_stats <- pheno_params %>%
group_by(species) %>%
summarise(
n = n(),
mean_los = mean(los, na.rm = TRUE),
sd_los = sd(los, na.rm = TRUE),
mean_ndvi = mean(ndvi_max, na.rm = TRUE)
)
print(summary_stats)
#> # A tibble: 5 × 5
#> species n mean_los sd_los mean_ndvi
#> <chr> <int> <dbl> <dbl> <dbl>
#> 1 Fagus_sylvatica 180 183. 9.40 0.796
#> 2 Pinus_pinaster 180 196. 8.85 0.782
#> 3 Pinus_sylvestris 180 195. 5.81 0.749
#> 4 Quercus_ilex 180 193. 13.0 0.859
#> 5 Quercus_petraea 180 186. 10.9 0.822
# Calculate multidimensional plasticity index
mpi_result <- multidim_plasticity(
data = demo_data,
traits = c("insect_size", "insect_resistance", "fungus_growth", "fungus_virulence"),
environments = "temperature",
groups = "tree_species"
)
cat("## Multidimensional Plasticity Index Results\n")
#> ## Multidimensional Plasticity Index Results
print(mpi_result$multidimensional_index)
#> [1] 0.3686611
# Create a formatted table
if (!is.null(mpi_result$multidimensional_index)) {
mpi_table <- as.data.frame(mpi_result$multidimensional_index)
kable(mpi_table, caption = "Plasticity Indices by Group") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
}
Plasticity Indices by Group
|
mpi_result$multidimensional_index
|
|
0.3686611
|
# Network plot
p1 <- plot_multidim_plasticity(
multidim_result = mpi_result,
type = "network",
plot_options = list(
title = "Plasticity Network",
node_size = 15,
edge_width = 2
)
)
# Landscape plot
p2 <- plot_multidim_plasticity(
multidim_result = mpi_result,
type = "landscape",
plot_options = list(
title = "Plasticity Landscape",
color_palette = "viridis"
)
)
# Combine plots
p1 + p2 + plot_annotation(title = "Multidimensional Plasticity Visualization")

# Extract individual plasticity - CORREGIDO
ind_plasticity <- mpi_result$individual_plasticity
cat("## Estructura de ind_plasticity\n")
#> ## Estructura de ind_plasticity
str(ind_plasticity)
#> tibble [5 × 5] (S3: tbl_df/tbl/data.frame)
#> $ tree_species : chr [1:5] "Tree_A" "Tree_B" "Tree_C" "Tree_D" ...
#> $ insect_size : num [1:5] 0.292 0.316 0.291 0.254 0.258
#> $ insect_resistance: num [1:5] 0.241 0.222 0.226 0.259 0.229
#> $ fungus_growth : num [1:5] 0.615 0.575 0.671 0.752 0.601
#> $ fungus_virulence : num [1:5] 0.294 0.372 0.303 0.269 0.335
if (!is.null(ind_plasticity)) {
# Convertir a formato largo para visualización
ind_plasticity_long <- ind_plasticity %>%
pivot_longer(
cols = -tree_species,
names_to = "trait",
values_to = "plasticity_value"
)
cat("\n## Datos en formato largo:\n")
print(head(ind_plasticity_long))
# Create a summary by group and trait
plasticity_summary <- ind_plasticity_long %>%
group_by(tree_species, trait) %>%
summarise(
mean_plasticity = mean(plasticity_value, na.rm = TRUE),
sd_plasticity = sd(plasticity_value, na.rm = TRUE),
n = n()
) %>%
ungroup()
cat("## Plasticity Summary by Species and Trait\n")
print(plasticity_summary)
# Visualization 1: Boxplot por especie (combinando todos los rasgos)
p1 <- ggplot(ind_plasticity_long, aes(x = tree_species, y = plasticity_value, fill = tree_species)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.5) +
labs(
title = "Plasticity Scores by Tree Species (all traits combined)",
x = "Tree Species",
y = "Plasticity Index"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_fill_viridis_d()
print(p1)
# Visualization 2: Heatmap por especie y rasgo
p2 <- ggplot(ind_plasticity_long, aes(x = tree_species, y = trait, fill = plasticity_value)) +
geom_tile(color = "white") +
scale_fill_viridis_c(option = "C") +
labs(
title = "Plasticity Index by Tree Species and Trait",
x = "Tree Species",
y = "Trait",
fill = "Plasticity Index"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p2)
# Visualization 3: Gráfico de barras por rasgo
p3 <- ggplot(ind_plasticity_long, aes(x = trait, y = plasticity_value, fill = tree_species)) +
geom_bar(stat = "identity", position = position_dodge()) +
labs(
title = "Plasticity Index by Trait and Tree Species",
x = "Trait",
y = "Plasticity Index",
fill = "Tree Species"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_fill_viridis_d()
print(p3)
} else {
cat("ind_plasticity es NULL\n")
}
#>
#> ## Datos en formato largo:
#> # A tibble: 6 × 3
#> tree_species trait plasticity_value
#> <chr> <chr> <dbl>
#> 1 Tree_A insect_size 0.292
#> 2 Tree_A insect_resistance 0.241
#> 3 Tree_A fungus_growth 0.615
#> 4 Tree_A fungus_virulence 0.294
#> 5 Tree_B insect_size 0.316
#> 6 Tree_B insect_resistance 0.222
#> ## Plasticity Summary by Species and Trait
#> # A tibble: 20 × 5
#> tree_species trait mean_plasticity sd_plasticity n
#> <chr> <chr> <dbl> <dbl> <int>
#> 1 Tree_A fungus_growth 0.615 NA 1
#> 2 Tree_A fungus_virulence 0.294 NA 1
#> 3 Tree_A insect_resistance 0.241 NA 1
#> 4 Tree_A insect_size 0.292 NA 1
#> 5 Tree_B fungus_growth 0.575 NA 1
#> 6 Tree_B fungus_virulence 0.372 NA 1
#> 7 Tree_B insect_resistance 0.222 NA 1
#> 8 Tree_B insect_size 0.316 NA 1
#> 9 Tree_C fungus_growth 0.671 NA 1
#> 10 Tree_C fungus_virulence 0.303 NA 1
#> 11 Tree_C insect_resistance 0.226 NA 1
#> 12 Tree_C insect_size 0.291 NA 1
#> 13 Tree_D fungus_growth 0.752 NA 1
#> 14 Tree_D fungus_virulence 0.269 NA 1
#> 15 Tree_D insect_resistance 0.259 NA 1
#> 16 Tree_D insect_size 0.254 NA 1
#> 17 Tree_E fungus_growth 0.601 NA 1
#> 18 Tree_E fungus_virulence 0.335 NA 1
#> 19 Tree_E insect_resistance 0.229 NA 1
#> 20 Tree_E insect_size 0.258 NA 1



# Create reaction norm plot - CORREGIDO
reaction_plot <- plot_reaction_norm(
data = demo_data,
environment = temperature,
trait = insect_size,
genotype = tree_species,
add_points = TRUE,
add_se = TRUE
# Eliminar line_colors ya que no existe
)
# Verificar si la función devuelve un objeto ggplot
if (inherits(reaction_plot, "gg")) {
# Enhance the plot
reaction_plot <- reaction_plot +
labs(
title = "Reaction Norms: Insect Size Across Temperatures",
subtitle = "Different tree species show varying plastic responses",
x = "Temperature (°C)",
y = "Insect Size (mm)",
color = "Tree Species"
) +
theme_minimal(base_size = 14) +
scale_color_viridis_d()
print(reaction_plot)
} else {
cat("plot_reaction_norm no devolvió un objeto ggplot. Tipo de retorno:", class(reaction_plot), "\n")
}

hp_result <- host_pathogen_interaction(
data = tree_insect_fungi,
host_trait = "tree_health",
pathogen_trait = "fungus_virulence",
environment = "temperature",
host_group = "tree_species",
pathogen_group = "fungi_strain"
)
cat("## Host-Pathogen Interaction Results\n")
#> ## Host-Pathogen Interaction Results
print(hp_result)
#> $host_plasticity
#> [1] 0.3167379
#>
#> $pathogen_plasticity
#> [1] 0.2529834
#>
#> $difference
#> [1] 0.06375452
#>
#> $interpretation
#> [1] "Host shows moderately higher plasticity"
#>
#> $details
#> $details$host
#> # A tibble: 5 × 4
#> tree_species mean_across_envs sd_across_envs cv
#> <chr> <dbl> <dbl> <dbl>
#> 1 Tree_A 0.653 0.218 0.334
#> 2 Tree_B 0.639 0.205 0.320
#> 3 Tree_C 0.648 0.203 0.313
#> 4 Tree_D 0.660 0.196 0.297
#> 5 Tree_E 0.625 0.199 0.319
#>
#> $details$pathogen
#> # A tibble: 4 × 4
#> fungi_strain mean_across_envs sd_across_envs cv
#> <chr> <dbl> <dbl> <dbl>
#> 1 Fungi_1 0.716 0.175 0.245
#> 2 Fungi_2 0.526 0.137 0.261
#> 3 Fungi_3 0.800 0.160 0.200
#> 4 Fungi_4 0.439 0.134 0.306
# Visualize the relationship
if (!is.null(hp_result$interaction_data)) {
ggplot(hp_result$interaction_data,
aes(x = tree_health, y = fungus_virulence, color = temperature)) +
geom_point(size = 3, alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE) +
facet_wrap(~tree_species) +
labs(
title = "Host-Pathogen Interaction Patterns",
x = "Tree Health",
y = "Fungus Virulence",
color = "Temperature"
) +
scale_color_gradient(low = "blue", high = "red")
}
# Check if extended function exists - VERSIÓN CORREGIDA
if (exists("host_pathogen_interaction_extended")) {
cat("## Ejecutando host_pathogen_interaction_extended...\n")
tryCatch({
hp_extended <- host_pathogen_interaction_extended(
host_data = tree_insect_fungi,
pathogen_data = tree_insect_fungi,
host_traits = c("tree_health", "tree_defense_response"),
pathogen_traits = c("fungus_growth", "fungus_virulence"),
environments = "temperature",
host_group = "tree_species",
pathogen_group = "fungi_strain"
)
cat("## Mostrando resultados disponibles...\n")
# Crear un análisis alternativo ya que los componentes están vacíos
cat("\n## ANÁLISIS ALTERNATIVO DE INTERACCIÓN HUÉSPED-PATÓGENO\n")
# Calcular correlaciones manualmente
interaction_corrs <- tree_insect_fungi %>%
group_by(tree_species, fungi_strain) %>%
summarise(
mean_tree_health = mean(tree_health, na.rm = TRUE),
mean_fungus_growth = mean(fungus_growth, na.rm = TRUE),
mean_tree_defense = mean(tree_defense_response, na.rm = TRUE),
mean_fungus_virulence = mean(fungus_virulence, na.rm = TRUE)
) %>%
ungroup()
cat("### Estadísticas descriptivas de interacciones:\n")
print(summary(interaction_corrs %>% select(-tree_species, -fungi_strain)))
# Gráfico 1: Correlación entre salud del árbol y crecimiento del hongo
p1 <- ggplot(interaction_corrs, aes(x = mean_tree_health, y = mean_fungus_growth)) +
geom_point(aes(color = tree_species, shape = fungi_strain), size = 4, alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "blue") +
labs(
title = "Interacción: Salud del Árbol vs Crecimiento del Hongo",
x = "Salud del Árbol (promedio)",
y = "Crecimiento del Hongo (promedio)",
color = "Especie de Árbol",
shape = "Cepa de Hongo"
) +
theme_minimal() +
theme(legend.position = "right")
print(p1)
# Gráfico 2: Correlación entre defensa del árbol y virulencia del hongo
p2 <- ggplot(interaction_corrs, aes(x = mean_tree_defense, y = mean_fungus_virulence)) +
geom_point(aes(color = tree_species, shape = fungi_strain), size = 4, alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "red") +
labs(
title = "Interacción: Defensa del Árbol vs Virulencia del Hongo",
x = "Respuesta de Defensa del Árbol (promedio)",
y = "Virulencia del Hongo (promedio)",
color = "Especie de Árbol",
shape = "Cepa de Hongo"
) +
theme_minimal() +
theme(legend.position = "right")
print(p2)
# Tabla de correlaciones calculadas
correlation_matrix <- cor(
interaction_corrs %>% select(mean_tree_health, mean_fungus_growth,
mean_tree_defense, mean_fungus_virulence),
use = "complete.obs"
)
cat("\n### Matriz de Correlación Calculada:\n")
print(correlation_matrix)
}, error = function(e) {
cat("Error en host_pathogen_interaction_extended:", e$message, "\n")
})
} else {
cat("## La función host_pathogen_interaction_extended NO existe\n")
}
#> ## Ejecutando host_pathogen_interaction_extended...
#> ## Mostrando resultados disponibles...
#>
#> ## ANÁLISIS ALTERNATIVO DE INTERACCIÓN HUÉSPED-PATÓGENO
#> ### Estadísticas descriptivas de interacciones:
#> mean_tree_health mean_fungus_growth mean_tree_defense mean_fungus_virulence
#> Min. :0.5907 Min. : 8.958 Min. :0.4223 Min. :0.4104
#> 1st Qu.:0.6276 1st Qu.:11.426 1st Qu.:0.5071 1st Qu.:0.5017
#> Median :0.6435 Median :13.620 Median :0.5688 Median :0.6098
#> Mean :0.6448 Mean :13.966 Mean :0.5625 Mean :0.6208
#> 3rd Qu.:0.6591 3rd Qu.:16.035 3rd Qu.:0.6278 3rd Qu.:0.7456
#> Max. :0.6984 Max. :19.830 Max. :0.6582 Max. :0.8415


#>
#> ### Matriz de Correlación Calculada:
#> mean_tree_health mean_fungus_growth mean_tree_defense
#> mean_tree_health 1.0000000 0.2248694 0.5674701
#> mean_fungus_growth 0.2248694 1.0000000 0.3753233
#> mean_tree_defense 0.5674701 0.3753233 1.0000000
#> mean_fungus_virulence -0.1378660 -0.9365441 -0.4529945
#> mean_fungus_virulence
#> mean_tree_health -0.1378660
#> mean_fungus_growth -0.9365441
#> mean_tree_defense -0.4529945
#> mean_fungus_virulence 1.0000000
trends <- analyze_temporal_trends(pheno_params)
cat("## Temporal Trend Analysis\n")
#> ## Temporal Trend Analysis
cat("Estructura de trends:\n")
#> Estructura de trends:
str(trends)
#> tibble [75 × 8] (S3: tbl_df/tbl/data.frame)
#> $ species : chr [1:75] "Fagus_sylvatica" "Fagus_sylvatica" "Fagus_sylvatica" "Fagus_sylvatica" ...
#> $ site : chr [1:75] "SITE_0001" "SITE_0002" "SITE_0003" "SITE_0004" ...
#> $ sos_slope: Named num [1:75] 0.899 1.862 2.398 0.685 2.438 ...
#> ..- attr(*, "names")= chr [1:75] "year" "year" "year" "year" ...
#> $ sos_p : num [1:75] 0.51735 0.32146 0.04042 0.25501 0.00428 ...
#> $ eos_slope: Named num [1:75] 0.114 -1.69 -0.328 1.456 -1.035 ...
#> ..- attr(*, "names")= chr [1:75] "year" "year" "year" "year" ...
#> $ eos_p : num [1:75] 0.882 0.327 0.772 0.163 0.396 ...
#> $ los_slope: Named num [1:75] -0.785 -3.552 -2.726 0.771 -3.474 ...
#> ..- attr(*, "names")= chr [1:75] "year" "year" "year" "year" ...
#> $ los_p : num [1:75] 0.5407 0.0193 0.1445 0.5768 0.0083 ...
# Si trends es un data frame, mostramos las primeras filas
if (is.data.frame(trends)) {
cat("Primeras filas de trends:\n")
print(head(trends, 10))
# Identificar columnas numéricas que podrían representar la pendiente
numeric_cols <- names(trends)[sapply(trends, is.numeric)]
cat("Columnas numéricas en trends:", paste(numeric_cols, collapse = ", "), "\n")
# Si hay una columna llamada 'slope' o similar, la usamos
slope_col <- NULL
if ("slope" %in% names(trends)) {
slope_col <- "slope"
} else if (any(grepl("slope", names(trends), ignore.case = TRUE))) {
slope_col <- names(trends)[grepl("slope", names(trends), ignore.case = TRUE)][1]
} else if (length(numeric_cols) > 0) {
# Tomamos la primera columna numérica que no sea año o similar
exclude <- c("year", "sos", "eos", "los", "ndvi_max", "gpp_total", "mean_temperature", "total_precipitation")
slope_col <- setdiff(numeric_cols, exclude)[1]
}
if (!is.null(slope_col)) {
cat("Usando la columna", slope_col, "como pendiente.\n")
# Renombrar la columna a 'slope' para facilitar
trends <- trends %>% rename(slope = !!sym(slope_col))
# Visualize trends
if (!is.null(trends) && nrow(trends) > 0) {
# Select top trends for visualization
top_trends <- trends %>%
filter(abs(slope) > quantile(abs(slope), 0.75, na.rm = TRUE)) %>%
head(10)
if (nrow(top_trends) > 0) {
p <- ggplot(top_trends, aes(x = reorder(species, slope), y = slope, fill = slope > 0)) +
geom_col() +
coord_flip() +
labs(
title = "Strongest Phenological Trends",
x = "Species",
y = "Trend Slope",
fill = "Direction"
) +
scale_fill_manual(values = c("FALSE" = "red", "TRUE" = "blue"))
print(p)
} else {
cat("No se encontraron tendencias fuertes.\n")
}
}
} else {
cat("No se pudo encontrar una columna de pendiente en trends.\n")
}
} else {
cat("trends no es un data frame. Tipo:", class(trends), "\n")
}
#> Primeras filas de trends:
#> # A tibble: 10 × 8
#> species site sos_slope sos_p eos_slope eos_p los_slope los_p
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Fagus_sylvatica SITE_00… 0.899 0.517 0.114 0.882 -0.785 0.541
#> 2 Fagus_sylvatica SITE_00… 1.86 0.321 -1.69 0.327 -3.55 0.0193
#> 3 Fagus_sylvatica SITE_00… 2.40 0.0404 -0.328 0.772 -2.73 0.144
#> 4 Fagus_sylvatica SITE_00… 0.685 0.255 1.46 0.163 0.771 0.577
#> 5 Fagus_sylvatica SITE_00… 2.44 0.00428 -1.04 0.396 -3.47 0.00830
#> 6 Fagus_sylvatica SITE_00… -0.605 0.698 0.395 0.807 1.00 0.598
#> 7 Fagus_sylvatica SITE_00… 0.654 0.573 0.124 0.941 -0.530 0.804
#> 8 Fagus_sylvatica SITE_00… 0.263 0.774 2.24 0.0545 1.97 0.173
#> 9 Fagus_sylvatica SITE_00… 3.02 0.00746 0.964 0.582 -2.06 0.354
#> 10 Fagus_sylvatica SITE_00… -0.196 0.774 -0.106 0.932 0.0903 0.947
#> Columnas numéricas en trends: sos_slope, sos_p, eos_slope, eos_p, los_slope, los_p
#> Usando la columna sos_slope como pendiente.

cat("## Ejecutando analyze_correlations...\n")
#> ## Ejecutando analyze_correlations...
corr_result <- analyze_correlations(pheno_params)
if (!is.null(corr_result)) {
# Mostrar las correlaciones principales
cat("### Top 10 correlaciones:\n")
if ("top_correlations" %in% names(corr_result)) {
top_corr_table <- corr_result$top_correlations %>%
mutate(
Correlation = round(Freq, 3),
Significance = ifelse(abs(Freq) > 0.6, "***",
ifelse(abs(Freq) > 0.4, "**",
ifelse(abs(Freq) > 0.2, "*", "")))
) %>%
select(Var1, Var2, Correlation, Significance)
print(top_corr_table)
}
# Crear y MOSTRAR el heatmap
if ("plot_data" %in% names(corr_result)) {
# Crear el heatmap
heatmap_plot <- ggplot(corr_result$plot_data,
aes(x = Var1, y = Var2, fill = Freq)) +
geom_tile(color = "white", linewidth = 0.5) +
geom_text(aes(label = ifelse(Var1 == Var2, "",
sprintf("%.2f", Freq))),
color = "black", size = 3.5) +
scale_fill_gradient2(
low = "#2E86AB", # Azul para negativas
mid = "#F6F5AE", # Amarillo claro para cero
high = "#A23B72", # Rojo para positivas
midpoint = 0,
limit = c(-1, 1),
name = "Correlación"
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
axis.text.y = element_text(size = 10),
axis.title = element_blank(),
panel.grid = element_blank(),
plot.title = element_text(face = "bold", hjust = 0.5)
) +
labs(
title = "Matriz de Correlación - Parámetros Fenológicos",
subtitle = "Los valores muestran coeficientes de correlación de Pearson"
) +
coord_fixed(ratio = 1)
# IMPRIMIR el gráfico
print(heatmap_plot)
# Agregar interpretación
cat("\n### Interpretación de correlaciones:\n")
cat("- Valores cercanos a +1: fuerte correlación positiva\n")
cat("- Valores cercanos a -1: fuerte correlación negativa\n")
cat("- Valores cercanos a 0: poca o ninguna correlación\n")
} else if ("matrix" %in% names(corr_result)) {
# Si solo hay matriz, crear heatmap desde ella
corr_matrix <- corr_result$matrix
# Convertir a formato largo
corr_melt <- as.data.frame(as.table(corr_matrix))
names(corr_melt) <- c("Var1", "Var2", "Freq")
heatmap_plot <- ggplot(corr_melt, aes(x = Var1, y = Var2, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = ifelse(Var1 == Var2, "",
sprintf("%.2f", Freq))),
size = 3) +
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
midpoint = 0, limit = c(-1, 1)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
labs(title = "Matriz de Correlación") +
coord_fixed()
print(heatmap_plot)
}
} else {
cat("## analyze_correlations devolvió NULL\n")
}
#> ### Top 10 correlaciones:
#> Var1 Var2 Correlation Significance
#> 1 los eos 0.644 ***
#> 2 eos los 0.644 ***
#> 3 eos sos 0.608 ***
#> 4 sos eos 0.608 ***
#> 5 ndvi_max sos -0.567 **
#> 6 sos ndvi_max -0.567 **
#> 7 ndvi_max eos -0.528 **
#> 8 eos ndvi_max -0.528 **
#> 9 los sos -0.216 *
#> 10 sos los -0.216 *

#>
#> ### Interpretación de correlaciones:
#> - Valores cercanos a +1: fuerte correlación positiva
#> - Valores cercanos a -1: fuerte correlación negativa
#> - Valores cercanos a 0: poca o ninguna correlación
# Prepare subset for seasonal curves
pheno_subset <- pheno_timeseries %>%
filter(year == 2021) %>%
group_by(species) %>%
slice_head(n = 100) %>% # Limit for clarity
ungroup()
if (exists("plot_seasonal_curves")) {
seasonal_plot <- plot_seasonal_curves(
time_series = pheno_subset,
selected_species = unique(pheno_subset$species)[1:5],
selected_year = 2021,
n_populations = 2
)
print(seasonal_plot)
}

cat("\n", "=", 70, "\n", sep = "")
#>
#> =70
cat("CREATING MULTIPLE HEATMAPS\n")
#> CREATING MULTIPLE HEATMAPS
cat("=", 70, "\n\n")
#> = 70
# Define parameters to plot (check existence)
params_to_plot <- c()
if ("los" %in% names(pheno_params)) params_to_plot <- c(params_to_plot, "los")
if ("sos" %in% names(pheno_params)) params_to_plot <- c(params_to_plot, "sos")
if ("eos" %in% names(pheno_params)) params_to_plot <- c(params_to_plot, "eos")
if ("ndvi_max" %in% names(pheno_params)) params_to_plot <- c(params_to_plot, "ndvi_max")
cat("Parameters to plot:", paste(params_to_plot, collapse = ", "), "\n\n")
#> Parameters to plot: los, sos, eos, ndvi_max
# Create and save each heatmap individually
for (i in seq_along(params_to_plot)) {
param <- params_to_plot[i]
cat(sprintf("%d. Creating heatmap for %s...\n", i, param))
tryCatch({
heatmap_plot <- plot_parameter_heatmap(
pheno_params,
parameter = param,
title = paste(toupper(param), "Heatmap")
)
# Save individually
filename <- paste0("heatmap_", param, ".png")
ggplot2::ggsave(filename, heatmap_plot, width = 10, height = 8, dpi = 300)
cat(" ✓ Saved as", filename, "\n")
# Display in RStudio
print(heatmap_plot)
}, error = function(e) {
cat(" ✗ Failed:", e$message, "\n")
})
}
#> 1. Creating heatmap for los...
#> ✓ Saved as heatmap_los.png
#> 2. Creating heatmap for sos...

#> ✓ Saved as heatmap_sos.png
#> 3. Creating heatmap for eos...

#> ✓ Saved as heatmap_eos.png
#> 4. Creating heatmap for ndvi_max...

#> ✓ Saved as heatmap_ndvi_max.png

cat("\n✓ Process completed\n")
#>
#> ✓ Process completed
if (exists("plot_density_ridges")) {
density_plot <- plot_density_ridges(
parameters = pheno_params,
parameter = "los",
group_by = "species",
title = "Length of Season Distribution by Species"
)
print(density_plot)
}

# 9. Análisis Espacial y Mapas Fenológicos
cat("# 9. Análisis Espacial y Mapas Fenológicos\n\n")
#> # 9. Análisis Espacial y Mapas Fenológicos
# Cargar datos espaciales - VERSIÓN CORREGIDA PARA TSV
if (file.exists("pheno_spatial_synthetic.csv")) {
# El archivo tiene formato TSV (separado por tabulaciones), no CSV
cat("## Leyendo archivo TSV (separado por tabulaciones)...\n")
# Leer con separador de tabulación
spatial_data <- read.delim("pheno_spatial_synthetic.csv",
sep = "\t", # Separador de tabulación
header = TRUE,
stringsAsFactors = FALSE,
check.names = FALSE) # Mantener nombres originales
cat("## Datos Espaciales Cargados Exitosamente\n")
cat("## Dimensiones:", dim(spatial_data), "\n")
cat("## Nombres de columnas:\n")
print(names(spatial_data))
cat("\n## Primeras filas (primeras 6 columnas):\n")
print(head(spatial_data[, 1:min(6, ncol(spatial_data))], 3))
# Verificar que tenemos las columnas necesarias
# Basado en la salida, parece que los nombres están correctos
# 9.1. Mapa básico de ubicaciones
cat("\n## 9.1. Mapa de Ubicaciones de Sitios\n")
# Verificar que tenemos coordenadas
if ("latitude" %in% names(spatial_data) && "longitude" %in% names(spatial_data)) {
# Convertir a numérico
spatial_data$latitude <- as.numeric(spatial_data$latitude)
spatial_data$longitude <- as.numeric(spatial_data$longitude)
# Filtrar coordenadas válidas
spatial_data <- spatial_data %>%
filter(!is.na(latitude), !is.na(longitude),
latitude >= -90, latitude <= 90,
longitude >= -180, longitude <= 180)
if (nrow(spatial_data) > 0) {
# Determinar variable para color
if ("forest_type" %in% names(spatial_data)) {
color_var <- spatial_data$forest_type
color_label <- "Tipo de Bosque"
is_categorical <- TRUE
} else if ("phenology_class" %in% names(spatial_data)) {
color_var <- spatial_data$phenology_class
color_label <- "Clase Fenológica"
is_categorical <- TRUE
} else {
color_var <- "Sitios"
color_label <- "Sitios"
is_categorical <- TRUE
}
# Determinar variable para tamaño
if ("los" %in% names(spatial_data)) {
size_var <- as.numeric(spatial_data$los)
size_label <- "Longitud de Estación (días)"
has_size <- TRUE
} else if ("elevation" %in% names(spatial_data)) {
size_var <- as.numeric(spatial_data$elevation)
size_label <- "Elevación (m)"
has_size <- TRUE
} else if ("productivity" %in% names(spatial_data)) {
size_var <- as.numeric(spatial_data$productivity)
size_label <- "Productividad"
has_size <- TRUE
} else {
size_var <- 3
size_label <- ""
has_size <- FALSE
}
# Crear mapa base
library(maps)
p1 <- ggplot(spatial_data, aes(x = longitude, y = latitude)) +
borders("world", colour = "gray70", fill = "gray95", size = 0.3) +
coord_quickmap() +
labs(
title = "Distribución Espacial de Sitios de Monitoreo Fenológico",
subtitle = paste("Total de sitios:", nrow(spatial_data)),
x = "Longitud",
y = "Latitud"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
# Añadir puntos según el tipo de variable de color
if (is_categorical) {
# Variable categórica
p1 <- p1 +
geom_point(aes(color = color_var, size = size_var),
alpha = 0.7, shape = 16) +
scale_color_viridis_d(
name = color_label,
option = "D"
)
} else {
# Variable numérica
p1 <- p1 +
geom_point(aes(color = color_var, size = size_var),
alpha = 0.7, shape = 16) +
scale_color_viridis_c(
name = color_label,
option = "C"
)
}
# Añadir escala de tamaño si es relevante
if (has_size) {
p1 <- p1 +
scale_size_continuous(
name = size_label,
range = c(3, 8),
breaks = pretty(range(size_var, na.rm = TRUE), n = 4)
)
} else {
p1 <- p1 + scale_size_identity()
}
# Añadir etiquetas si hay pocos puntos
if (nrow(spatial_data) <= 20 && "site_name" %in% names(spatial_data)) {
p1 <- p1 +
geom_text(aes(label = site_name),
vjust = -0.8, size = 2.5, color = "black", alpha = 0.7)
}
print(p1)
# 9.2. Mapa de calor para variables continuas
cat("\n## 9.2. Mapa de Calor para Variables Continuas\n")
# Lista de variables numéricas para visualizar
numeric_vars <- c()
if ("los" %in% names(spatial_data)) numeric_vars <- c(numeric_vars, "los")
if ("ndvi_max" %in% names(spatial_data)) numeric_vars <- c(numeric_vars, "ndvi_max")
if ("productivity" %in% names(spatial_data)) numeric_vars <- c(numeric_vars, "productivity")
if ("elevation" %in% names(spatial_data)) numeric_vars <- c(numeric_vars, "elevation")
if ("mean_annual_temp" %in% names(spatial_data)) numeric_vars <- c(numeric_vars, "mean_annual_temp")
if (length(numeric_vars) > 0) {
# Seleccionar la primera variable numérica disponible
map_var <- numeric_vars[1]
map_label <- switch(map_var,
"los" = "Longitud de Estación (días)",
"ndvi_max" = "NDVI Máximo",
"productivity" = "Productividad",
"elevation" = "Elevación (m)",
"mean_annual_temp" = "Temperatura Media Anual (°C)",
map_var)
p2 <- ggplot(spatial_data, aes(x = longitude, y = latitude)) +
borders("world", colour = "gray70", fill = "gray95", size = 0.3) +
geom_point(aes(color = as.numeric(.data[[map_var]])),
size = 5, alpha = 0.8) +
scale_color_viridis_c(
option = "B",
name = map_label
) +
coord_quickmap() +
labs(
title = paste("Distribución de", map_label),
subtitle = "Color indica valor de la variable",
x = "Longitud",
y = "Latitud"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
print(p2)
# Crear mapa interpolado si hay suficientes puntos
if (nrow(spatial_data) >= 5 && requireNamespace("akima", quietly = TRUE)) {
cat("\n## 9.3. Mapa Interpolado\n")
tryCatch({
library(akima)
# Preparar datos para interpolación
interp_data <- spatial_data %>%
filter(!is.na(.data[[map_var]]),
!is.na(longitude),
!is.na(latitude)) %>%
select(longitude, latitude, value = .data[[map_var]])
if (nrow(interp_data) >= 4) {
# Crear grid para interpolación
lon_range <- range(interp_data$longitude, na.rm = TRUE)
lat_range <- range(interp_data$latitude, na.rm = TRUE)
lon_seq <- seq(lon_range[1], lon_range[2], length.out = 40)
lat_seq <- seq(lat_range[1], lat_range[2], length.out = 40)
# Interpolación
interp_result <- interp(
x = interp_data$longitude,
y = interp_data$latitude,
z = interp_data$value,
xo = lon_seq,
yo = lat_seq,
linear = TRUE,
extrap = FALSE
)
# Convertir a data frame
interp_df <- expand.grid(
longitude = interp_result$x,
latitude = interp_result$y
)
interp_df$value <- as.vector(interp_result$z)
p3 <- ggplot() +
borders("world", colour = "gray70", fill = "gray95", size = 0.3) +
geom_tile(data = interp_df,
aes(x = longitude, y = latitude, fill = value),
alpha = 0.7) +
geom_point(data = spatial_data,
aes(x = longitude, y = latitude),
size = 2, color = "black", shape = 1) +
geom_contour(data = interp_df,
aes(x = longitude, y = latitude, z = value),
color = "white", alpha = 0.5, bins = 8) +
scale_fill_viridis_c(
option = "B",
name = map_label
) +
coord_quickmap() +
labs(
title = paste("Mapa Interpolado de", map_label),
subtitle = "Isolíneas muestran valores similares",
x = "Longitud",
y = "Latitud"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
print(p3)
}
}, error = function(e) {
cat("Interpolación no disponible:", e$message, "\n")
})
}
}
# 9.4. Mapas por categoría
cat("\n## 9.4. Mapas por Categoría\n")
# Encontrar variables categóricas
categorical_vars <- c()
if ("forest_type" %in% names(spatial_data)) categorical_vars <- c(categorical_vars, "forest_type")
if ("land_cover" %in% names(spatial_data)) categorical_vars <- c(categorical_vars, "land_cover")
if ("climate_zone" %in% names(spatial_data)) categorical_vars <- c(categorical_vars, "climate_zone")
if ("phenology_class" %in% names(spatial_data)) categorical_vars <- c(categorical_vars, "phenology_class")
if (length(categorical_vars) > 0) {
cat_var <- categorical_vars[1]
cat_label <- switch(cat_var,
"forest_type" = "Tipo de Bosque",
"land_cover" = "Cobertura del Suelo",
"climate_zone" = "Zona Climática",
"phenology_class" = "Clase Fenológica",
cat_var)
# Contar categorías para determinar layout
n_categories <- length(unique(spatial_data[[cat_var]]))
n_cols <- ifelse(n_categories <= 4, 2, 3)
p4 <- ggplot(spatial_data, aes(x = longitude, y = latitude)) +
borders("world", colour = "gray70", fill = "gray95", size = 0.3) +
geom_point(aes(color = if ("los" %in% names(spatial_data)) as.numeric(los) else 1,
size = if ("productivity" %in% names(spatial_data)) as.numeric(productivity) else 3),
alpha = 0.7) +
facet_wrap(as.formula(paste("~", cat_var)),
ncol = n_cols,
scales = "fixed") +
scale_color_viridis_c(
option = "A",
name = ifelse("los" %in% names(spatial_data), "LOS (días)", "Valor")
) +
coord_quickmap() +
labs(
title = paste("Distribución Espacial por", cat_label),
x = "Longitud",
y = "Latitud"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
strip.text = element_text(face = "bold", size = 10),
strip.background = element_rect(fill = "gray90", color = "gray70")
)
if ("productivity" %in% names(spatial_data)) {
p4 <- p4 +
scale_size_continuous(name = "Productividad", range = c(2, 6))
} else {
p4 <- p4 + scale_size_identity()
}
print(p4)
}
# 9.5. Análisis de correlaciones espaciales
cat("\n## 9.5. Análisis de Correlaciones Espaciales\n")
# Crear lista de variables numéricas para análisis
analysis_vars <- c()
if ("los" %in% names(spatial_data)) analysis_vars <- c(analysis_vars, "los")
if ("ndvi_max" %in% names(spatial_data)) analysis_vars <- c(analysis_vars, "ndvi_max")
if ("productivity" %in% names(spatial_data)) analysis_vars <- c(analysis_vars, "productivity")
if ("elevation" %in% names(spatial_data)) analysis_vars <- c(analysis_vars, "elevation")
if ("mean_annual_temp" %in% names(spatial_data)) analysis_vars <- c(analysis_vars, "mean_annual_temp")
if (length(analysis_vars) >= 2) {
# Calcular matriz de correlación
numeric_data <- spatial_data[, analysis_vars]
# Convertir todas a numérico
numeric_data <- as.data.frame(lapply(numeric_data, as.numeric))
cor_matrix <- cor(numeric_data, use = "complete.obs")
cat("### Matriz de Correlación:\n")
print(cor_matrix)
# Visualizar correlaciones
cor_melt <- as.data.frame(as.table(cor_matrix))
names(cor_melt) <- c("Var1", "Var2", "Correlation")
p5 <- ggplot(cor_melt, aes(x = Var1, y = Var2, fill = Correlation)) +
geom_tile(color = "white", size = 1) +
geom_text(aes(label = sprintf("%.2f", Correlation)),
color = ifelse(abs(cor_melt$Correlation) > 0.7, "white", "black"),
size = 4) +
scale_fill_gradient2(
low = "#2E86AB", # Azul para negativas
mid = "#F6F5AE", # Amarillo para cero
high = "#A23B72", # Rojo para positivas
midpoint = 0,
limits = c(-1, 1),
name = "Correlación"
) +
labs(
title = "Correlaciones entre Variables Espaciales",
x = "",
y = ""
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
axis.text.y = element_text(size = 10),
panel.grid = element_blank()
) +
coord_fixed()
print(p5)
}
# 9.6. Gráficos de relación con latitud
cat("\n## 9.6. Relaciones con Latitud\n")
if (length(analysis_vars) > 0) {
for (var in analysis_vars) {
if (var %in% names(spatial_data)) {
p <- ggplot(spatial_data, aes(x = latitude, y = as.numeric(.data[[var]]))) +
geom_point(aes(color = longitude), size = 3, alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "red", alpha = 0.2) +
scale_color_viridis_c(name = "Longitud") +
labs(
title = paste("Relación entre Latitud y", var),
x = "Latitud (grados)",
y = var
) +
theme_minimal() +
theme(plot.title = element_text(face = "bold", hjust = 0.5))
print(p)
# Calcular correlación
cor_test <- cor.test(spatial_data$latitude,
as.numeric(spatial_data[[var]]),
use = "complete.obs")
cat(sprintf("- Correlación latitud-%s: r = %.3f (p = %.4f)\n",
var, cor_test$estimate, cor_test$p.value))
}
}
}
# 9.7. Resumen estadístico
cat("\n## 9.7. Resumen Estadístico Espacial\n")
cat("### Estadísticas de coordenadas:\n")
cat(sprintf("- Rango de latitud: %.2f a %.2f grados\n",
min(spatial_data$latitude), max(spatial_data$latitude)))
cat(sprintf("- Rango de longitud: %.2f a %.2f grados\n",
min(spatial_data$longitude), max(spatial_data$longitude)))
cat(sprintf("- Número de sitios: %d\n", nrow(spatial_data)))
if ("elevation" %in% names(spatial_data)) {
cat(sprintf("- Rango de elevación: %.0f a %.0f m\n",
min(as.numeric(spatial_data$elevation), na.rm = TRUE),
max(as.numeric(spatial_data$elevation), na.rm = TRUE)))
}
# Información por categoría si existe
if (length(categorical_vars) > 0) {
cat("\n### Distribución por categoría:\n")
for (cat_var in categorical_vars) {
if (cat_var %in% names(spatial_data)) {
cat_table <- table(spatial_data[[cat_var]])
if (length(cat_table) <= 10) { # Solo mostrar si hay pocas categorías
cat(paste("\n- Por", cat_var, ":\n"))
print(cat_table)
}
}
}
}
} else {
cat("## No hay datos con coordenadas válidas después del filtrado.\n")
}
} else {
cat("## No se encontraron columnas 'latitude' y 'longitude' en los datos.\n")
cat("## Columnas disponibles:", paste(names(spatial_data), collapse = ", "), "\n")
}
} else {
cat("## Archivo pheno_spatial_synthetic.csv no encontrado.\n")
}
#> ## Archivo pheno_spatial_synthetic.csv no encontrado.
# Simulate plasticity data for testing
if (exists("simulate_plasticity_data")) {
sim_data <- simulate_plasticity_data(
n_genotypes = 5,
n_environments = 3,
n_traits = 4,
plasticity_patterns = c("gradient", "gradient", "threshold", "optimal")
)
cat("## Simulated Data Structure\n")
if (!is.null(sim_data)) {
cat("Dimensions:", dim(sim_data), "\n")
cat("Variables:", paste(names(sim_data), collapse = ", "), "\n")
# Quick visualization of simulated patterns
if ("environment" %in% names(sim_data) && "trait1" %in% names(sim_data)) {
ggplot(sim_data, aes(x = environment, y = trait1, group = genotype, color = genotype)) +
geom_line(size = 1) +
geom_point(size = 2) +
labs(
title = "Simulated Plasticity Patterns",
x = "Environment",
y = "Trait Value"
) +
theme_minimal()
}
}
}
#> ## Simulated Data Structure
#> Dimensions: 45 7
#> Variables: genotype, environment, replicate, trait_1, trait_2, trait_3, trait_4
cat("## Package Summary\n")
#> ## Package Summary
cat("The phenop package provides comprehensive tools for:\n\n")
#> The phenop package provides comprehensive tools for:
cat("1. **Multidimensional Plasticity Analysis**\n")
#> 1. **Multidimensional Plasticity Analysis**
cat(" - Calculate MPI indices\n")
#> - Calculate MPI indices
cat(" - Visualize plasticity networks\n")
#> - Visualize plasticity networks
cat(" - Analyze individual plasticity\n\n")
#> - Analyze individual plasticity
cat("2. **Reaction Norm Analysis**\n")
#> 2. **Reaction Norm Analysis**
cat(" - Plot genotype × environment interactions\n")
#> - Plot genotype × environment interactions
cat(" - Calculate reaction norm slopes\n\n")
#> - Calculate reaction norm slopes
cat("3. **Host-Pathogen Interactions**\n")
#> 3. **Host-Pathogen Interactions**
cat(" - Analyze co-evolutionary dynamics\n")
#> - Analyze co-evolutionary dynamics
cat(" - Detect interaction patterns\n\n")
#> - Detect interaction patterns
cat("4. **Phenological Analysis**\n")
#> 4. **Phenological Analysis**
cat(" - Detect temporal trends\n")
#> - Detect temporal trends
cat(" - Analyze correlations\n")
#> - Analyze correlations
cat(" - Seasonal pattern visualization\n\n")
#> - Seasonal pattern visualization
cat("5. **Advanced Visualizations**\n")
#> 5. **Advanced Visualizations**
cat(" - Heatmaps, networks, and landscapes\n")
#> - Heatmaps, networks, and landscapes
cat(" - Seasonal curves and density plots\n\n")
#> - Seasonal curves and density plots
cat("6. **Statistical Analysis**\n")
#> 6. **Statistical Analysis**
cat(" - Trade-off detection\n")
#> - Trade-off detection
cat(" - Environmental optimization\n")
#> - Environmental optimization
cat(" - PCA and anomaly detection\n\n")
#> - PCA and anomaly detection
cat("## Recommended Workflow\n")
#> ## Recommended Workflow
cat("1. Start with `multidim_plasticity()` for initial assessment\n")
#> 1. Start with `multidim_plasticity()` for initial assessment
cat("2. Use `plot_reaction_norm()` to visualize G×E interactions\n")
#> 2. Use `plot_reaction_norm()` to visualize G×E interactions
cat("3. Apply `analyze_temporal_trends()` for phenological data\n")
#> 3. Apply `analyze_temporal_trends()` for phenological data
cat("4. Check for trade-offs with `plasticity_tradeoffs_extended()`\n")
#> 4. Check for trade-offs with `plasticity_tradeoffs_extended()`
cat("5. Use `run_complete_analysis()` for comprehensive pipeline\n")
#> 5. Use `run_complete_analysis()` for comprehensive pipeline