El objetivo de este documento es mostrar como usar el paquete de R
para la base de datos Neotoma, neotoma2
.
La librería neotoma2 está disponible en GitHub y actualmente la documentación sólo está en inglés - trabajamos para tenerla en español próximamente.
Los objetivos de aprendizaje de este taller son:
Aprender cómo se organiza la información dentro de Neotoma (Sitios, Unidades de colección y Conjuntos de datos)
Cómo se traduce esta estructura en el paquete neotoma2 para R:
Si estás planeando trabajar con Neotoma, te invitamos a unirte a nuestro canal de Slack, donde tenemos un espacio específico para preguntas sobre el paquete en R: el canal #it_r (también hay #it_r_es si prefieres ayuda en español).
También puedes unirte a la comunidad de Neotoma a través de nuestras listas de correo en Google Groups. Para hacerlo, revisa la información en nuestro sitio web o contáctanos para ser agregado.
Los datos en la base de datos de Neotoma están organizados como un conjunto de relaciones conectadas que permiten representar los distintos elementos de un análisis paleoecológico:
La paleoecología es un campo amplio y en constante evolución por lo que estas relaciones pueden ser bastante complejas. Por eso, la base de datos está altamente estructurada y normalizada, lo que permite agregar nuevos conocimientos sin afectar el modelo de datos central.
Si quieres profundizar en cómo está organizada la base de datos, puedes leer el Manual de la base de datos de Neotoma o revisar directamente el esquema del modelo relacional.
En Neotoma, los datos se asocian a sitios – ubicaciones específicas con coordenadas de latitud y longitud.
Dentro de un sitio, puede haber una o más unidades de colecta – que son los puntos específicos donde se recolectan físicamente las uestras dentro del sitio. Por ejemplo:
Las unidades de colección pueden tener coordenadas GPS de mayor resolución que el sitio general, pero siguen considerándose parte del mismo sitio.
Los datos dentro de una colección son recopilados a partir de diferentes unidades de análisis, por ejemplo:
Los datos recolectados en una unidad de análisis se agrupan según su tipo de conjunto de datos (carbón, diatomeas, dinoflagelados, etc.) y se organizan como una muestra. El conjunto de muestras de una colección para un tipo de datos determinado se asigna a un conjunto de datos.
neotoma2
plotLeaflet()
puede ser utilizada en un objeto de
la clase sites
.Si observamos el diagrama
UML podemos observar que la estructura de datos en el paquete
neotoma2
refleja la estructura original de la base de
datos.
Como veremos en la sección Búsqueda de Sitios, podemos buscar estos objetos y empezar a trabajar con ellos (en la sección Análisis Simple).
Un punto que debemos tener en cuenta, en el paquete
neotoma2
, la mayoría de los objetos son de la clase
sites
, que contienen mayor o menor información. Hay
diferentes funciones que operan sobre sites
. Cuando
queremos obtener mayor información sobre los sitios sites
,
utilizamos las funciones get_datasets()
o
get_downloads()
que pueblan con mayor información los
sitios.
get_sites()
Hay diferentes maneras de encontrar sitios en neotoma2
.
Debémos pensar en los sitios
como objetos espaciales.
Tienen nombre, ubicación y pueden ser encontrados bajo en contexto de
unidades geopolíticas. Sin embargo, bajo el contexto de la API y del
paquete de R, los sitios en sí mismos no contienen datos sobre la
taxonomía, el grupo de datos o las edades. Simplemente es un contenedor
al que le podemos agregar más información. Es así que cuando buscamos
por sitio, lo hacemos usando los siguientes atributos (en inglés):
Parámetro | Descripción |
---|---|
sitename | Un nombre válido, usando % como comodín. |
siteid | Un identificador numérico único del sitio en la base de datos de Neotoma. |
loc | Un vector de caja delimitadora, GeoJSON o cadena WKT. |
altmin | Límite inferior de altitud para los sitios. |
altmax | Límite superior de altitud para los sitios. |
database | La base de datos específica de donde se extraen los registros. |
datasettype | El tipo de conjunto de datos (ver
get_tables(datasettypes) ). |
datasetid | Identificador numérico único del conjunto de datos en Neotoma. |
doi | Un DOI válido para un conjunto de datos en Neotoma. |
gpid | Un ID numérico único o cadena de texto que identifica una unidad geopolítica. |
keywords | Palabras clave únicas para muestras registradas en Neotoma. |
contacts | Un nombre o ID numérico de personas asociadas con los sitios. |
taxa | IDs numéricos únicos o nombres de taxones asociados con los sitios. |
sitename="%Lago%"
Hay ocasiones en las que sabremos exactamente el nombre del sitio que estamos buscando (“Lago Titicaca”), y habrà ocasiones en las que tendremos una idea aproximada sobre el nombre (por ejemplo, sabemos que el nombre es parecido a “Lago Titicaca”, o “Lake Titicaca”, pero no estamos seguros de como fue ingresado a la base de datos).
De forma general, utilizamos el formato:
get_sites(sitename="XXXXX")
para buscar un sitio por
nombre.
PostgreSQL (y la API) utilizan el signo de porcentaje como comodín.
De esta forma, "%Titicaca%"
seleccionará “Lake Titicaca” y en caso de
existir, también seleccionaría “Lago Titicaca” y “Pantano
Titicaca”. La búsqueda tampoco distingue entre
mayúsculas y minúsculas, por lo que simplemente podría escribir
"%titicaca%"
.
lago_titicaca <- neotoma2::get_sites(sitename = "%titicaca%")
lago_titicaca
## siteid
## 14104
## 14202
## 24301
## sitename
## Lago Huiñaimarca - Lake Titicaca [Lago Menor][Lago Pequeno][Lago Winaymarca]
## Lake Titicaca
## Lago Huiñaimarca - Lake Titicaca [Lago Menor][Lago Pequeno][Lago Winaymarca]
## lat long altitude
## -16.28412 -68.72699 3811
## -16.12887 -69.50345 3809
## -16.29474 -68.73378 3811
loc=c()
El paquete neotoma
utilizaba un cuadro delimitador para
buscar por ubicación. El cuadro estaba estructurado como un vector con
valores de latitud y longitud: c(xmin, ymin, xmax, ymax)
.
En neotoma2
se puede utilizar esta misma caja delimitadora
o podemos definir objetos espaciales más complejos con el paquete sf
. El
paquete sf
nos permite trabajar con datos ráster y
polígonos en R, para seleccionar sitios existentes en objetos espaciales
más complejos. El parametro loc
trabaja con vectores
simples, objetos WKT,
objetos geoJSON y
objectos sf
en R. Notar que el paquete
neotoma2
es un función contenedora API que utiliza un URL
(api.neotomadb.org).
Buscar sitios utilizando una ubicación. En el siguiente código hay
tres representaciones de Sudamérica: geoJSON, WKT y con un cuadro
delimitador. También hemos transformado el elemento
sa$geoJSON
a un objeto del paquete sf
. Podemos
utilizar cualquiera de estas cuatro representaciones para trabajar con
el paquete neotoma2
.
sa <- list(geoJSON = '{"type": "Polygon",
"coordinates": [[
[-79.66, -5.97],
[-70.06, -19.07],
[-74.38, -55.59],
[-34.67, -6.52],
[-76.41, 8.37],
[-79.66, -5.97]
]]}',
WKT = 'POLYGON ((-79.66, -5.97,
-70.06, -19.07,
-74.38, -55.59,
-34.67, -6.52,
-76.41, 8.37,
-79.66, -5.97))',
bbox = c(-79.66, -55.59, -34.67, 8.37))
sa$sf <- geojsonsf::geojson_sf(sa$geoJSON)
sa_sites <- neotoma2::get_sites(loc = sa$sf, all_data = TRUE)
Puedes siempre hacer un gráfico de los sites
obtenidos
con plot()
, pero los datos perderan el contexto geográfico.
La función plotLeaflet()
regresa un mapa de la librería
leaflet()
y permite mayor personalización o agregar datos
espaciales adicionales (como nuestro cuadro delimitador,
sa$sf
, que funciona directamente con el paquete
leaflet
):
Note the use of the %>%
pipe here. If you are not
familiar with this symbol, check our “Piping in
R” section of the Appendix.
neotoma2::plotLeaflet(sa_sites) %>%
leaflet::addPolygons(map = .,
data = sa$sf,
color = "green")
Si observamos al diagrama
UML para los objetos de neotoma2
podemos ver que hay un
conjunto de funciones qeu operan a nivel de sites
(sitios).
Conforme vamos agregando información a los objetos sites
mediante las funciones get_datasets()
o
get_downloads()
, podemos utilizar un mayor número de
funciones auxiliares. Podemos así, tomar ventaja de funciones como
summary()
para tener un mejor entendimiento de los
diferentes tipos de datos que tenemos en este conjunto de sitios. El
código a continuación regresa la tabla de resumen. Hacemos después un
poco de magia con R para cambiar el formato en que los datos están
siendo representados (convirtiéndolo a un objeto
datatable()
), pero la pieza principal es la llamada a la
función summary()
.
# Give information about the sites themselves, site names &cetera.
neotoma2::summary(sa_sites)
# Give the unique identifiers for sites, collection units and datasets found at those sites.
neotoma2::getids(sa_sites)
Podemos ver que no hay cronologías asociadas con el objeto
sites
. Esto es porque, por el momento, no hemos extraído la
información necesaria de los dataset
. Todo lo que sabemos,
tras la llamada get_sites()
son los tipos de conjuntos de
datos con los que contamos.
get_datasets()
):Sabemos que las colecciones
y los
conjuntos de datos
están contenidos en los
sitios
. Similarmente, un objeto de tipo sites
contienen collectionunits
que contienen
datasets
. En la tabla anterior podemos ver que algunos de
los sitios contienen registros de diatomeas. Dicho esto, solo tenemos la
información de sites
, pero por conveniencia, la API
devuelve información adicional sobre los conjuntos de datos lo que nos
permite navegar de manera más fácil los registros.
Con un objeto sites
podemos llamar directamente a la
función get_datasets()
, que nos permitirá extraer metadatos
sobre los conjuntos de datos. Podemos utilizar la función
datasets()
en cualqueir momento para obtener más
información de los conjuntos de datos que un objeto sites
pueda contener. Comparemos la información impresa
datasets(sa_sites)
contra una llamada similar utilizando el
siguiente código.
# This may be slow, because there's a lot of sites!
sa_datasets <- neotoma2::get_datasets(sa_sites, all_data = TRUE)
datasets(sa_datasets)
filter()
Si decidimos únicamente obtener registros de un sólo tipo de datos, o
si requerimos de mayor filtración, debemos considerar filtrar antes de
descargar todos los datos y muestras. Para ello, utilizaremos la función
filter()
. Por ejemplo, si requerimos únicamente los
registros de polen, podemos filtrar de la siguiente forma:
sa_pollen <- sa_sites %>%
neotoma2::filter(datasettype == "pollen")
neotoma2::summary(sa_pollen)
Podemos ver que aún faltan las cronologías para estos registros. Para obtener esta información, debemos hacer otras funciones que recolecten esta informacion desde la API.
sample()
.Debido a que los datos de las muestras agregan mucha sobrecarga (para
Sudamérica, los datos de polen sobrecargara nuestro sites
object más que 20 veces), por eso llamamos la función
get_downloads()
después de haber hecho un filtrado
preliminar. Después de get_datasets()
, tenemos información
sufciente para filtar basados en ubicación, límites de tiempo y tipo
conjunto de datos. Cuando ejecutamosget_downloads()
podemos
hacer un filtrado más fino a nivel de unidad de análisis o nivel de
taxón.
El siguiente comando puede tomar algo de tiempo. Por eso, hemos guardado el resultado en un archivo RDS. Puedes intentar correr este comando por tu cuenta o puedes cargar el archivo RDS.
## This line is commented out because we've already run it for you.
sa_dl <- sa_pollen %>% get_downloads(all_data = TRUE)
## .......................................................................................................................................................................................................................................
saveRDS(sa_dl, "data/saDownload.RDS")
sa_dl <- readRDS("data/saDownload.RDS")
Una vez que hemos hecho la descarga, ahora tenemos información de
cada sitio asociado a las unidades de colecta, los tipos de conjunto de
datos, y a todas las muestras asociadas a estos conjuntos. Para extraer
toda las muestras, utilizamos la función samples
:
allSamp <- samples(sa_dl)
Una vez hecho esto, obtenemos un data.frame
esto es una
tabla con nrow(allSamp) renglones y
ncol(allSamp) columnas. La razón de que esta tabla sea
muy larga es porque estamos obteniendo los datos en un formato
largo. Cada rengón contiene toda la información que se
necesita para interpretarse correctamente:
## [1] "age" "agetype" "ageolder" "ageyounger"
## [5] "chronologyid" "chronologyname" "units" "value"
## [9] "context" "element" "taxonid" "symmetry"
## [13] "taxongroup" "elementtype" "variablename" "ecologicalgroup"
## [17] "analysisunitid" "sampleanalyst" "sampleid" "depth"
## [21] "thickness" "samplename" "datasetid" "database"
## [25] "datasettype" "age_range_old" "age_range_young" "age_units"
## [29] "datasetnotes" "siteid" "sitename" "lat"
## [33] "long" "area" "sitenotes" "description"
## [37] "elev" "collunitid"
Para algunos tipos de conjunto de datos o análisis específicos,
algunas columnas podrán no ser necesarias. Sin embargo, para otros
conjuntos de datos pueden ser críticamente importantes. Para permitir
que el paquete neotoma2
sea lo más útil posible para todos
los usuarios, hemos incluido todas las columnas posibles.
Si quieres saber que taxones existen en los registros, puedes
utilizar la función taxa()
en el objeto sites
.
La función taxa()
regresa los taxones únicos junto con dos
columnas adicionales sites
y samples
que
indican en cuantos sitios y en cuantas muestras el taxón aparece, esto
nos ayuda a comprender mejor que tan común es cada taxón individual.
neotomatx <- neotoma2::taxa(sa_dl)
Las taxonomías en Neotoma no siempre son tan directas como podríamos pensar. La identificación taxonómica en paleoecología puede ser compleja y verse influenciada por la morfología del organismo, el estado de conservación del palinomorfo, la experiencia del/la analista, entre otros factores. Puedes leer más sobre este tema en la sección sobre Conceptos Taxonómicos del Manual de Neotoma.
En la base de datos utilizamos identificadores únicos (por ejemplo,
taxonid
, siteid
, analysisunitid
)
porque nos permiten conectar los distintos registros entre sí. Los
valores de taxonid
que devuelve la función
taxa()
se pueden vincular con la columna
taxonid
en la tabla que devuelve samples()
.
Esto nos permite, por ejemplo, crear tablas de armonización taxonómica
si lo necesitamos.
También notarás que el nombre del taxón (taxonname
)
aparece en el campo variablename
. En Neotoma, los conteos
individuales de muestras se reportan como variables
.
Una “variable” puede representar una especie, una medición de
laboratorio o incluso un proxy no orgánico, como carbón (charcoal) o
mediciones de fluorescencia de rayos X (XRF). Estas variables incluyen
tanto la unidad de medida como el valor correspondiente.
Podemos utilizar paquetes como rioja
para hacer trazados
estratigráficos para un único registro. Pero primero tenemos que hacer
un manejo de datos diferente. A pesar de que podríamos hacer
armonización nuevamente, vamos a tomar los 10 taxones más comúnes en un
sitio dado los trazaremos en un diagrama estratigráfico.
Utilizaremos la función arrange()
para ordenar confrome
al número de veces que un taxón aparece en un núcleo. De esta forma,
podemos tomar las muestras y seleccionar los taxones que aparecen en las
diez primeras filas del marco de datos plottingTaxa
.
# Get a particular site, in this case we are simply subsetting the
# `sa_dl` object to get Lake Solsø:
plottingSite <- sa_dl[[1]]
counts <- plottingSite %>%
samples() %>%
toWide(ecologicalgroup = c("TRSH"),
unit = c("NISP"),
elementtypes = c("pollen"),
groupby = "age",
operation = "prop")
counts <- counts[, colSums(counts > 0.01, na.rm = TRUE) > 5]
counts
## # A tibble: 36 × 11
## age Cecropia Byrsonima Alchornea `Urticaceae/Moraceae` Melastomataceae
## <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 969 0.0323 0.0323 0.0645 0.323 0.484
## 2 989 0 0 0.190 0.333 0.381
## 3 1010 0 0 0.273 0.273 0.227
## 4 1031 0.0435 0.0435 0.174 0.261 0.435
## 5 1051 0 0 0.263 0.368 0.316
## 6 1072 0.05 0 0 0.2 0.45
## 7 1093 0 0 0.2 0.333 0.4
## 8 1113 0.05 0.05 0.1 0.35 0.25
## 9 1134 0.143 0 0.179 0.25 0.179
## 10 1155 0.125 0.0833 0.167 0.292 0.292
## # ℹ 26 more rows
## # ℹ 5 more variables: Coccoloba <dbl>, Myrtaceae <dbl>,
## # `Anacardiaceae cf. Spondias` <dbl>, Curatella <dbl>, Celtis <dbl>
Aparentemente, esto es una llamada compleja de comandos. Sin embargo,
la función toWide()
proporciona control sobre los taxones,
unidades y otros elementos para que puedan ser ingresados en una matriz
(depth
x taxon
) que muchas herramientas
estadísticas como los paquetes vegan
o rioja
usan.
Para crear gráficas, podemos usar strat.plot()
del
paquete rioja
, ordenar los taxones usando puntajes promedio
ponderados (wa.order
). También se ha agregado un gráfico
CONISS al borde del gráfico, para mostrar cómo funciona el nuevo marco
de datos amplio con funciones métricas de distancia.
clust <- rioja::chclust(dist(sqrt(counts)),
method = "coniss")
# Plot the stratigraphic plot, converting proportions to percentages:
plot <- rioja::strat.plot(counts[,-1] * 100, yvar = counts$age,
title = sa_dl[[1]]$sitename,
ylabel = "Calibrated Years BP",
xlabel = "Pollen (% of Trees and Shrubs)",
y.rev = TRUE,
clust = clust,
wa.order = "topleft",
scale.percent = TRUE)
rioja::addClustZone(plot, clust, 4, col = "red")
Hemos hecho muchas cosas en este ejemplo. - Buscamos sitios utilizando nombres y parámetros geográficos. - Filtramos resultados utilizando parámetros espaciales y temporales. - Obtuvimos información de las muestras de los conjuntos de datos seleccionados. - Hicimos un análisis estratográfico básico.
¡Esperamos que utilizen estos ejemplos como un bloque para usar en el futuro en su trabajo o para algo nuevo y divertido!
install.packages('neotoma2')
library(neotoma2)
Sin embargo, para instalar la versión más actualizada, es recomendable hacer (versión 1.0.6):
install.packages('devtools')
devtools::install_github("NeotomaDB/neotoma2@dev")
library(neotoma2)
Para las liberías adicionales, puedes usar pacman. Si no tienes
pacman
instalado, puedes instalarlo usando el siguiente
comando:
options(warn = -1)
install.packages("pacman")
pacman::p_load(neotoma2, dplyr, ggplot2, sf, geojsonsf, leaflet, terra, DT, readr, stringr, rioja)
Aunque se puede trabajar de forma local, este tutorial se puede hacer utilizando la instancia de Binder. Binder ejecutará una sesión de RStudio en tu navegador, con todas las liberías necesarias.
O bien, para una versión más amena, se puede utilizar la instancia de Github-pages.
Estas aquí.
R
Las pipas o pipes en R son una forma de encadenar funciones.
Generalmente, se puede utilizar los operadores: |>
or
%>%
. |>
es nativo en R y
%>%
viene del ecosistema tidyverse
en R. En
neotoma2
usamos %>%
.
Este operador es muy útil pues funciona como una tubería que lleva agua de un lugar a otro. En programación, es como una línea de montaje donde los datos entran por una función, se modifican y el resultado es ingresado a la siguiente función. Esto hace que el código sea legible y más facil de escribir. También reduce el número de variables que se necesitan, ahorrando espacio en memoria.
Por ejemplo, sin los pipes en neotoma2
para extraer un
sitio y luego crear la gráfica haríamos:
# Retrieve the site
plot_site <- neotoma2::get_sites(sitename = "%ø%")
# Plot the site
neotoma2::plotLeaflet(object = plot_site)
Es decir, primero creamos la variable plot_site
que sólo
utilizaríamos una vez pero que fue necesaria para poder ejecutar la
función plotLeaflet
.
Con la pipa (%>%
) no necesitamos crear la variable,
simplemente escribimos nuestro código. Así plotLeaflet()
no
requiere un argumento object
porque utiliza la respuesta
generada por get_sites(sitename = "%ø%")
.
# Retrieve the site
neotoma2::get_sites(sitename = "%ø%") %>%
plotLeaflet()