Tutorial: Descargar y limpiar datos de GBIF para múltiples especies 🌳
Objetivo:
Aprender a descargar datos de ocurrencia desde GBIF para una o varias especies, combinarlos en un solo conjunto de datos, limpiar coordenadas con CoordinateCleaner, y eliminar registros que se encuentren dentro de áreas urbanas usando datos de OpenStreetMap.
🧩 1) Preparar el entorno
Instala y carga los paquetes necesarios:
install.packages(c(
"readxl", "tidyr", "dplyr", "rgbif", "sf",
"rnaturalearth", "CoordinateCleaner", "osmdata", "mapview"
))
Luego cárgalos:
library(readxl)
library(tidyr)
library(dplyr)
library(rgbif)
library(sf)
library(rnaturalearth)
library(CoordinateCleaner)
library(osmdata)
library(mapview)
🌱 2) Descargar datos de GBIF para una especie
Para hacer descargas autenticadas en GBIF necesitas un usuario y contraseña registrados en gbif.org.
user <- "tu.usuario.gbif"
pwd <- "tu.contraseña.gbif"
email <- "tu.correo"
Ejemplo con una especie:
taxon_info <- name_backbone("Nothofagus pumilio")
taxon_key <- taxon_info$usageKey
gbif_download <- occ_download(
pred("taxonKey", taxon_key),
pred("country", "CL"),
pred("hasGeospatialIssue", FALSE),
pred("hasCoordinate", TRUE),
pred("occurrenceStatus", "PRESENT"),
pred_gte("year", 2000),
user = user, pwd = pwd, email = email,
format = "SIMPLE_CSV"
)
occ_download_wait(gbif_download)
data_downloaded <- occ_download_get(gbif_download) |>
occ_download_import()
##Revisa la tabla descargada
str(data_downloaded)
head(as.data.frame(data_downloaded))
🌿 3) Descargar datos para varias especies
Antes de automatizar la descarga para varias especies, es útil entender qué son los loops (bucles) en R. Un loop permite repetir un conjunto de instrucciones varias veces sin tener que escribirlas manualmente para cada elemento. Por ejemplo, si quisieras descargar los datos de cinco especies distintas, podrías escribir el mismo código cinco veces… pero con un loop puedes hacerlo de forma automatizada, una vez por cada especie en una lista.
En R, los loops más comunes son for, while y repeat. En este caso usaremos un bucle for, que recorre los elementos de una lista (por ejemplo, nombres de especies) y ejecuta el mismo bloque de código para cada uno.
arboles_rev <- read.csv("./arboles_chile_revisado.csv")
species_list <- sample(unique(arboles_rev$species), 5)
species_list
datasets_list <- list()
for (species_name in species_list) {
cat("Processing:", species_name, "\n")
taxon_info <- name_backbone(species_name)
taxon_key <- taxon_info$usageKey
gbif_download <- occ_download(
pred("taxonKey", taxon_key),
pred("country", "CL"),
pred("hasGeospatialIssue", FALSE),
pred("hasCoordinate", TRUE),
pred("occurrenceStatus", "PRESENT"),
pred_gte("year", 2000),
user = user, pwd = pwd, email = email,
format = "SIMPLE_CSV"
)
occ_download_wait(gbif_download)
data_downloaded <- occ_download_get(gbif_download) |>
occ_download_import()
datasets_list[[species_name]] <- data_downloaded
cat("Completed:", species_name, "\n")
}
⛔ 4) Vamos a combinar los datos
datasets_list
datos_juntos <- rbind(datasets_list)
Encontramos un error porque muchas veces las tablas descargadas no son iguales en sus características. Para resolver este error, tenemos que estandarizar las columnas de las tablas. Para hacer esto, lo más eficiente crear una función de pegado estandarizado. —
🧠 5) Crear una función para combinar los datos
En R, una función es un conjunto de instrucciones que se agrupan bajo un mismo nombre para reutilizarlas fácilmente. Por ejemplo, en lugar de copiar y pegar varias líneas de código cada vez que queremos combinar archivos, podemos definir una función que haga ese trabajo por nosotros. Esto permite mantener el código más limpio, ordenado y reutilizable, además de facilitar la detección de errores.
unificar_y_combinar_datasets <- function(lista_df) {
todas_columnas <- unique(unlist(lapply(lista_df, names)))
lista_df <- lapply(lista_df, function(df) {
faltantes <- setdiff(todas_columnas, names(df))
for (col in faltantes) df[[col]] <- NA
df <- df[, todas_columnas]
return(df)
})
tipos_por_columna <- lapply(lista_df, function(df) sapply(df, class))
tipos_df <- bind_rows(tipos_por_columna)
columnas_conflictivas <- names(tipos_df)[apply(tipos_df, 2, function(x) length(unique(x)) > 1)]
lista_df_limpia <- lapply(lista_df, function(df) {
for (col in columnas_conflictivas) df[[col]] <- as.character(df[[col]])
return(df)
})
bind_rows(lista_df_limpia)
}
💡 Recuerda: los paquetes son conjuntos de funciones que amplían las capacidades de R.
💾 6) Ahora podemos unir los datasets para guardarlos como una tabla
combined_data <- unificar_y_combinar_datasets(datasets_list)
write.csv(combined_data, "./combined_gbif_data.csv", row.names = FALSE)
🗺️ 7) Visualizar ocurrencias en un mapa interactivo
#primero convertimos el data.frame creado en un objeto espacial
ocurrencias_sf <- st_as_sf(
combined_data,
coords = c("decimalLongitude", "decimalLatitude"),
crs = 4326
)
#Además creamos un polígono para chile
chile <- ne_countries(country = "Chile", returnclass = "sf")
mapviewOptions(basemaps = c("OpenStreetMap", "Esri.WorldTopoMap", "CartoDB.Positron"))
mapview(chile, alpha.regions = 0.1, layer.name = "Chile") +
mapview(ocurrencias_sf, zcol = "species", cex = 5, alpha = 0.7)
🧹 8) Limpiar coordenadas con CoordinateCleaner
Si observamos los datos, podemos ver que hay ocurrencias que parecen tener errores. Estos errores deben ser limpiados. Para ello utilizaremos el paquete CoordinateCleaner (Zizka et al., 2019), una herramienta diseñada para detectar y estandarizar registros geográficos erróneos en datos de biodiversidad. Este paquete identifica automáticamente coordenadas sospechosas —como aquellas ubicadas en centros urbanos, museos, océanos o en el punto 0°N 0°E—, ayudando a mejorar la calidad y confiabilidad de los datos antes de su análisis.
clean_input <- combined_data %>%
select(
species = scientificName,
decimalLatitude,
decimalLongitude,
year
) %>%
mutate(iso_a2 = "CL")
cc_flags <- clean_coordinates(
x = clean_input,
lon = "decimalLongitude",
lat = "decimalLatitude",
species = "species",
countries = "iso_a2",
tests = c("capitals", "centroids", "equal", "gbif", "institutions",
"seas", "zeros", "urban"),
value = "flagged"
)
summary(cc_flags)
occs_limpias <- combined_data[cc_flags, ]
Visualización rápida del nuevo dataset limpio:
occs_limpias_sf <- st_as_sf(
occs_limpias,
coords = c("decimalLongitude", "decimalLatitude"),
crs = 4326
)
mapview(chile, alpha.regions = 0.1, layer.name = "Chile") +
mapview(occs_limpias_sf, zcol = "species", cex = 5, alpha = 0.7)
🏙️ 9) Excluir ocurrencias dentro de áreas urbanas (OpenStreetMap)
Parece razonable excluir arboles que puedan estar siendo cultivadas dentro de ciudades fuera del rango de distribución de las especies.
chile_bb <- getbb("Chile")
urb_osm <- opq(chile_bb) %>%
add_osm_feature(key = "landuse", value = "residential") %>%
osmdata_sf()
urb_osm_sf <- urb_osm$osm_polygons |> st_make_valid()
occs_limpias_sf$in_urban <- lengths(st_intersects(occs_limpias_sf, urb_osm_sf)) > 0
table(occs_limpias_sf$in_urban)
occ_sin_urb <- occs_limpias_sf |> filter(!in_urban)
🗺️ 10) Visualizar datos limpios y no urbanos
m_chile <- mapview(
chile,
alpha.regions = 0.1,
color = "gray30",
layer.name = "Chile"
)
m_urb <- mapview(
urb_osm_sf,
col.regions = "red",
alpha.regions = 0.3,
lwd = 0.3,
layer.name = "Áreas urbanas"
)
m_occ <- mapview(
occ_sin_urb,
zcol = "species",
cex = 5,
alpha = 0.7,
layer.name = "Ocurrencias sin urbanas"
)
m_chile + m_urb + m_occ
💡 Puedes activar o desactivar cada capa en la esquina superior derecha del mapa interactivo.
📘 Autor: Ricardo Segovia
🧩 Proyecto: Curso SENCE-IEB — Gestión y modelamiento de datos de biodiversidad
📅 Actualizado: Octubre 2025