Computando tendencias en una rejilla climática con Tidyverse

Lectura de un NetCDF al estilo Tidyverse!

Actualmente es posible leer un fichero NetCDF al estilo Tidyverse mediante el package creado por Elio Campitelli llamado metR. Además esta librería contiene muchisimas funciones útiles para la representación y el análisis de datos meteorológicos y climáticos.

En esta ocasión trabajaremos con una rejilla diaria de 5x5 km de temperatura máxima de las Islas Baleares (España) (Serrano-Notivoli 2019). Para la lectura usaremos un par de funciones del package metR: GlanceNetCDF, para echarle un vistazo al nombre de la variable, dimensiones, etc, y ReadNetCDF, para la lectura del fichero NetCDF. Lógicamente haremos uso de la librería tidyverse, la cual nos permitirá organizar los datos y realizar algun cálculo sencillo (recuerda que tidyverse es una colección de packages!), así como las librerias lubridate, para el manejo de fechas, y la librería trend para calcular la significación estadística de la tendencia de nuestros datos. Además usaremos la librería pals para escoger la paleta de colores cuando representemos los resultados gráficamente.

library(tidyverse)
library(lubridate)
library(metR)
library(trend)
library(pals) # paletas de colores

Como queremos trabjar los datos del NetCDF en formato Tidyverse, vamos a especificar que el output de la función ReadNetCDF sea un data.frame:

# Descargamos el fichero de Temperatura máxima (Tx) de Baleares 

# https://digital.csic.es/handle/10261/177655

# Extraemos información del NetCDF (nombre variable, dimensiones, etc)
file = "tmax_bal.nc"

variable <- GlanceNetCDF(file = file)
variable
## ----- Variables ----- 
## tx:
##     Maximum temperature in Celsius degrees
##     Dimensions: lon by lat by Time
## 
## 
## ----- Dimensions ----- 
##   lon: 63 values from 0.970530815018679 to 4.55413081501868 degrees_east
##   lat: 48 values from 38.3196292450381 to 40.4252292450381 degrees_north
##   Time: 16071 values from 1971-01-02 to 2015-01-01

# Leemos el NetCDF
tx <- ReadNetCDF(file = file,
                 vars = c(var = names(variable$vars[1])),# extracción del nombre de la variable.
                 out = "data.frame") # objeto de salida
tx # voilà.
##                 Time      lat       lon var
##        1: 1971-01-02 40.42523 0.9705308  NA
##        2: 1971-01-02 40.42523 1.0283308  NA
##        3: 1971-01-02 40.42523 1.0861308  NA
##        4: 1971-01-02 40.42523 1.1439308  NA
##        5: 1971-01-02 40.42523 1.2017308  NA
##       ---                                  
## 48598700: 2015-01-01 38.31963 4.3229308  NA
## 48598701: 2015-01-01 38.31963 4.3807308  NA
## 48598702: 2015-01-01 38.31963 4.4385308  NA
## 48598703: 2015-01-01 38.31963 4.4963308  NA
## 48598704: 2015-01-01 38.31963 4.5541308  NA

Gestión y manejo de los datos

A continuación el objetivo es agregar los datos a escala anual, eso es, pasar del dato diario al promedio anual. Para ello tenemos que jugar con varias funciones de la librería dplyr, cargada en el tidyverse. Primero, sin embargo, hay que filtrar los valores NA:

tx <- tx %>% filter(!is.na(var)) %>% as.tibble()
## Warning: `as.tibble()` is deprecated as of tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
tx
## # A tibble: 4,114,176 x 4
##    Time                  lat   lon   var
##    <dttm>              <dbl> <dbl> <dbl>
##  1 1971-01-02 00:00:00  40.1  3.92  7.64
##  2 1971-01-02 00:00:00  40.1  3.98  7.69
##  3 1971-01-02 00:00:00  40.1  4.03  7.44
##  4 1971-01-02 00:00:00  40.1  4.09  8.29
##  5 1971-01-02 00:00:00  40.1  4.15  8.15
##  6 1971-01-02 00:00:00  40.1  4.21  8.35
##  7 1971-01-02 00:00:00  40.0  3.80  8.10
##  8 1971-01-02 00:00:00  40.0  3.86  8.04
##  9 1971-01-02 00:00:00  40.0  3.92  7.63
## 10 1971-01-02 00:00:00  40.0  3.98  7.70
## # ... with 4,114,166 more rows

Una vez filtrados los NA, procedemos a realizar el agregado anual en cada punto de grid:

tx_annual <- tx %>%
  mutate(Time = as_date(Time)) %>% #convertimos las fechas en formato lubridate
  mutate(year = year(Time)) %>% # Creamos la columna de años para realizar agregado
  group_by(year, lon, lat) %>% # agrupamos por año, longitud y latitud
  summarise(annual_tx = mean(var)) %>% # Ahora solo falta realizar el promedio
  ungroup() # una vez terminamos, desagrupamos
## `summarise()` regrouping output by 'year', 'lon' (override with `.groups` argument)

tx_annual
## # A tibble: 11,520 x 4
##     year   lon   lat annual_tx
##    <dbl> <dbl> <dbl>     <dbl>
##  1  1971  1.20  38.9      20.8
##  2  1971  1.20  38.9      20.9
##  3  1971  1.26  38.9      20.2
##  4  1971  1.26  38.9      20.3
##  5  1971  1.26  38.9      20.8
##  6  1971  1.32  38.9      20.7
##  7  1971  1.32  38.9      20.6
##  8  1971  1.32  38.9      20.7
##  9  1971  1.32  39.0      21.0
## 10  1971  1.32  39.0      20.3
## # ... with 11,510 more rows

Como podemos observar, se muestra la temperatura máxima promedio para cada año y punto de grid.

Cálculo de la tendencia y de su significación estadística

De este modo, ya podemos calcular la tendencia anual y su significación estadística con la librería trend. Para ello emplearemos la función sens.slope para el cálculo de la pendiente de la tendencia (qué magnitud de temperatura aumenta/disminuye en un año); y la función mk.test (Test de Mann Kendall), para verificar si la tendencia es estadísticamente significativa o si realmente es fruto de la variabilidad de la propia serie termométrica. Además, al tratarse de un test no paramétrico, nuestros datos no tendrán por que responder a una distribución gausiana.

tx_trend <- tx_annual %>%
  group_by(lon,lat) %>% # Agrupamos por lon y lat para realizar el cálculo en cada celda
  summarise(slope = sens.slope(annual_tx)$estimates *10,
            sign = mk.test(annual_tx)$p.value)
## `summarise()` regrouping output by 'lon' (override with `.groups` argument)

tx_trend
## # A tibble: 256 x 4
## # Groups:   lon [38]
##      lon   lat slope   sign
##    <dbl> <dbl> <dbl>  <dbl>
##  1  1.20  38.9 0.122 0.156 
##  2  1.20  38.9 0.122 0.156 
##  3  1.26  38.9 0.110 0.115 
##  4  1.26  38.9 0.112 0.120 
##  5  1.26  38.9 0.147 0.0834
##  6  1.32  38.9 0.136 0.0983
##  7  1.32  38.9 0.133 0.111 
##  8  1.32  38.9 0.146 0.0869
##  9  1.32  39.0 0.150 0.0645
## 10  1.32  39.0 0.133 0.0983
## # ... with 246 more rows

Ya tenemos el valor del pendiente Sen, que nos indica la tasa de cambio cada 10 años (hemos multiplicado x 10), así como la significación de la tendencia indicada a través del p-value. Ahora simplemente realizaremos la visualización de los resultados indicando aquellas celdas que muestran una significación estadística al aumento o disminución de la temperatura máxima anual.

ggplot()+
  geom_tile(data = tx_trend, aes(x = lon, y = lat, fill = slope))+
  scale_fill_gradientn(colors = rev(pals::linearlhot(100)), name = "ºC/10y", limits = c(0.1,0.45)) +
  geom_point(data = filter(tx_trend, sign < 0.01),aes(x = lon, y = lat, color = "Tendencia sign. \n p-value <0.01"),
             size = 0.4, show.legend = T) +
  scale_color_manual(values = c("black"), name = "")+
  coord_fixed(1.3)+
  xlab("Longitud") + ylab("Latitud")+
  labs(title = "Tendencia decadal de la temperatura máxima en las Islas Baleares", 
       subtitle = "(1971-2014)",
       caption = "Base de datos: STEAD (Serrano-Notivoli,2019)")+
  theme_bw() +
  guides(fill = guide_colourbar(barwidth = 9, barheight = 0.5, title.position="right"))+
  theme(legend.position = "bottom")

Para cualquier duda, no dudes en dejar un comentario.

Saludos!

Marc Lemus-Canovas
Marc Lemus-Canovas
Investigador Postdoctoral

Soy un apasionado del tratamiento de datos climáticos, especialmente utilizando R!

Relacionado