Computant tendències en una graella climàtica amb Tidyverse

Lectura d’un NetCDF a l’estil Tidyverse!

Actualment és possible llegir un fitxer NetCDF a l’estil Tidyverse mitjançant el package creat per Elio Campitelli anomenat metR. A més, aquesta llibreria conté moltíssimes funcions útils per a la representació i l’anàlisi de dades meteorològiques i climàtiques. En aquesta ocasió treballarem amb una graella diària de 5x5 km de temperatura màxima de les Illes Balears (Serrano-Notivoli 2019). Per a la seva lectura farem ús d’un parell de funcions del package metR:GlanceNetCDF, ens permetrà fer una ullada al nom de la variable, dimensions, etc, i ReadNetCDF, realitzarà la lectura de l’arxiu NetCDF. Lògicament farem ús de la llibreria tidyverse, la qual ens permetrà organitzar les dades i realitzar algun càlcul senzill (recorda que el tidyverse és una col·lecció de packages!), Així com les llibreries lubridate, per a la gestió de dates, i la llibreria trend per calcular la significació estadística de la tendència. A més farem servir la llibreria pals per escollir la paleta de colors quan representem els resultats gràficament.

library(tidyverse)
library(lubridate)
library(metR)
library(trend)
library(pals) # paletes de colors

Al voler treballar amb les dades del NetCDF en format Tidyverse, especificarem que el output de la funció ReadNetCDF sigui un data.frame:

# Descarreguem el fitxer de Temperatura màxima (Tx) de les Balears 

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

# Extraiem informació de l'NetCDF (nom variable, dimensions, 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

# Llegim el NetCDF
tx <- ReadNetCDF(file = file,
                 vars = c(var = names(variable$vars[1])),# extracció del nom de la variable.
                 out = "data.frame") # objecte de sortida
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ó y manipulació de les dades

A continuació l’objectiu és agregar les dades a escala anual, això és, passar de la dada diària a la mitjana anual. Per a tal objectiu hem de jugar amb diverses funcions de la llibreria dplyr, carregada altidyverse. Primer, però, cal filtrar els valors 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

Un cop filtrats els NA, procedim a calcular les mitjanes anuals a cada punt de grid:

tx_annual <- tx %>%
  mutate(Time = as_date(Time)) %>% # convertim les dates en format lubridate
  mutate(year = year(Time)) %>% # Creem la columna d'anys per realitzar l'agregat
  group_by(year, lon, lat) %>% # agrupem per any, longitud i latitud
  summarise(annual_tx = mean(var)) %>% # Ara només falta fer la mitjana
  ungroup() # desagrupem
## `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

Com podem observar, es mostra la temperatura màxima mitjana per a cada any i punt de grid.

Càlcul de la tendència i de la seva significació estadística

D’aquesta manera, ja podem calcular la tendència anual i la seva significació estadística mitjançant la llibreria trend. Emprarem la funció sens.slope per al càlcul del pendent de la tendència (quina magnitud de temperatura augmenta / disminueix en un any); i la funció mk.test (Test de Mann Kendall), per verificar si la tendència és estadísticament significativa o si realment és fruit de la variabilitat de la pròpia sèrie termomètrica. A més, al tractar-se d’un test no paramètric, les nostres dades no hauran d’ajustar-se a una distribució gaussiana necessàriament.

tx_trend <- tx_annual %>%
  group_by(lon,lat) %>% # Agrupem per lon i lat per a realitzar el càlcul en cada cel·la
  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

Ja tenim el valor del pendent de Sen, que ens indica la taxa de canvi cada 10 anys (hem multiplicat x 10), així com la significació de la tendència indicada a través del p-value. Ara simplement realitzarem la visualització dels resultats indicant aquelles cel·les que mostren una significació estadística a l’augment o disminució 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 = "Tendència 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 = "Tendència decadal de la temperatura màxima a les Illes Balears", 
       subtitle = "(1971-2014)",
       caption = "Base de dades: STEAD (Serrano-Notivoli,2019)")+
  theme_bw() +
  guides(fill = guide_colourbar(barwidth = 9, barheight = 0.5, title.position="right"))+
  theme(legend.position = "bottom")

Per a qualsevol qüestió, no dubtis en deixar un comentari.

Salutacions!

Marc Lemus-Canovas
Marc Lemus-Canovas
Investigador Postdoctoral

Sóc un apassionat del tractament de dades climàtiques, especialment utilitzant R!