---
title: "Time Series Plotting"
author: "An-Chi Ho"
date: "2023-11-03"
output: html_document
vignette: >
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteIndexEntry{Time Series Plotting}
  %\usepackage[utf8]{inputenc}
---

# Time series plotting

In this vignette, we will go through the functions that plot the time series type of figures: 
`VizAno()`, `VizClim()`, `VizVsLtime()`, `Viz2VarsVsLTime()`, `VizBoxWhisker()`, `VizACC()`. 

We will use the data loaded in [Data Loading](data_loading.md). 
Please follow that vignette first to get the data if you want to replicate the results in this vignette. Load European region data with `region = "euro"` in the loading script.

All the functions devoted to plotting time series have some common traits and
parameters:
 - Aimed to plot monthly, seasonal or yearly time series, adjustable via the 
parameter `freq`.
 - All of them generate plots and save them to PostScript files the path and 
file name of which you have to provide to the `fileout` parameter. Can plot in
a presentation oriented style or in a paper oriented style, adjustable with the
parameter `biglab`.
 - Can plot a legend automatically, adjustable via the parameters `leg`, 
`listexp` and `listobs`/`listvar` in `VizVsLTime()`, `Viz2VarVsLtime()` and 
`VizClim()` or via the parameter `legends` in `VizAno()` and `VizACC()`.
 - Accept any additional parameters via the parameter `...` to be sent to 
the underlying R graphics `plot()` function for a fine tuning.

## Multi-member raw data or anomalies

`VizAno()` also takes one or two data arrays as inputs with 4 dimensions each:
```r
  c(n. of datasets, n. of members, n. of start dates, n. of forecast times)
```
The data in the provided arrays can be raw data, anomalies or any other kind
of data, as long as it is arranged with the required dimensions.  
If any dimension is missing, `s2dv::InsertDim` can insert it to make the function work.

For each dataset, a thick line with the ensemble mean is plotted 
together with a finer line for each member, unless any of these are 
disabled with `ensmean` or `memb`, respectively. 
The area delimited by the minimum and maximum ensemble values at each forecast time is filled with a 
different color for each start date, unless disabled with `fill`.  
A fine black line is plotted for the observational data alongside each 
starting date.
The function creates separate plots for each experimental dataset provided in the experimental data array.

The start dates of the data need to be provided with the parameter `sdates` for them to be plotted accordingly along the x axis. 

`linezero` draws a line at y = 0 to help perceiving whether the anomaly is 
positive or negative, and `vlines` draws vertical lines at any specified location. The parameter `points` can draw all the lines in pointed style.

We need to adjust the data arrays before putting them in `VizAno()`: Reorder the dimension order and insert missing dimension 'ensemble' for obs data.
```r
exp_sm <- s2dv::Reorder(exp_sm, c("dat", "ensemble", "sdate", "time"))
obs_sm <- s2dv::Reorder(obs_sm, c("dat", "sdate", "time"))
obs_sm <- s2dv::InsertDim(obs_sm, 2, 1, "ensemble")

sdates <- paste0(1997:2016, '1101')

VizAno(exp_sm, obs_sm, sdates,
       toptitle = paste0(c('SEAS5', 'Meteo-France System 7'), ": Raw 'tas' over Europe"),
       ytitle = c("K", "K"), legends = "ERA5",
       fileout = paste0("ts_raw_exp", 1:2, "_obs.png"))
```
<img src="./Figures/ts_raw_exp1_obs.png" width="600" />
<img src="./Figures/ts_raw_exp2_obs.png" width="600" />


## Climatologies

`VizClim()` takes an experimental and/or an observational array of 2 to 3 dimensions each as inputs:
```r
  c(n. of datasets, n. of members, n. of forecast times)
```
The latitude and longitude dimensions must have been removed either because working on area-averages or
because working on a single grid point. And the start dates dimension
must have also been removed by time average, i.e. climatologies.

A curve is plotted for each member of each experiment or observation. The 
members of the same dataset are colored with the same color whereas the 
different datasets are plotted with different colors.  
`VizClim()` assumes by default that the provided climatologies start in 
January. Otherwise the initial month can be specified with the parameter `monini`.

We firstly take the spatial mean data to calculate climatology by `s2dv::Clim`, then put the outputs in `VizClim()`.

```r
clim_sm <- s2dv::Clim(exp_sm, obs_sm, dat_dim = c("dat", "ensemble"), memb_dim = "ensemble")

VizClim(clim_sm$clim_exp, clim_sm$clim_obs, monini = 11,
         toptitle = "Per-pair 'tas' climatologies, Europe",
         ytitle = "K",
         listexp = c('SEAS5', 'Meteo-France System 7'), listobs = c('ERA5'),
         fileout = "ts_clim_exp1_exp2_obs.png")
```
<img src="./Figures/ts_clim_exp1_exp2_obs.png" width="600" />


## Statistics and scores

The functions `VizVsLTime()`, `Viz2VarsVsLTime()` and `VizACC()` serve to
plot time series of indices or scores that usually come 
together with confidence intervals and significance levels.
We use the statistical functions from package "s2dv" to process the data.


### VizVsLTime

`VizVsLTime()` takes as main input an array of data with the following dimensions:

```r
  c(n. of experiments, n. of observational datasets, 3 or 4, n. of forecast times)
```

The second dimension is optional, and the third one is corresponding to the lower 
limit of a confidence interval, the measurement or index or score, the upper
limit of a confidence interval and, optionally, a significance level. 
This dimension format requires that the index or score to be plotted is computed
along the actual time (start dates) and over the average of the ensemble member,
either at a single grid point or from an area average.

A curve is drawn for each pair of (experiment, observation) with a different 
color and line style. The scores or indices that corresponds to the same 
experiment are coloured equally, and a different line style is used for each 
observation that the experiment has been compared with. 
The confidence intervals are plotted with a finer line of the same color and
line style, unless disabled with `show_conf`. 
To plot the significance level instead, the parameter `siglev` can be set to `TRUE`.

The start month of each curve is January as default, and it can be adjusted by parameter `monini`. 
Additionally the number of ticks along the x axis can be adjusted with 
`nticks` and any number of horizontal lines can be drawn by specifying the 
target ordinates to `hlines`.

`VizVsLTime()` can be useful in the following situations:  
 1. To plot trend or regression fitted coefficients of ensemble-mean data  
 2. To plot the spread (i.e., interquartile range, maximum minus minimum, standard deviation  
or median absolute deviation) across ensemble members and starting dates  
 3. To plot the correlation, RMSE, and RMSSS between ensemble-mean experiments and observations  
 4. To plot the ratio of RMSE between two experiments
 5. To plot the ratio between the standard deviation and RMSE of the experiment
 6. To plot effective number of independent data  


#### 1. Trend and Regression

First, we use s2dv function `Ano_CrossValid` to get the anomalies of experimental data, 
`MeanDims` to calculate the ensemble mean, then use `Trend` to calculate the temporal trend of anomaly.

```r
# Calculate anomalies and trend
ano <- s2dv::Ano_CrossValid(exp_sm, obs_sm, memb = FALSE, dat_dim = c("dat", "ensemble"), memb_dim = "ensemble")

trend_exp <- s2dv::Trend(s2dv::MeanDims(ano$exp, 'ensemble'), time_dim = "sdate")
```

The output of `s2dv::Trend` has the following elements: `trend`, `conf.lower`, 
`conf.upper`, `p.val`, and `detrended`. The dimension `stats` of the first four elements
contains the regression coefficients from the lowest order (i.e., intercept) to the highest degree. 

`VizVsLTime` requires the input to have the dimensions (dat, 4, time) and the 4 elements 
together in the following order: `conf.lower`, slope , `conf.upper`, intercept. 
So, we need to resemble the trend output first. After that, we plot the trend 
along with the confidence level of two experiements in the same plot.

```r
input_trend <- array(dim = c(dat = 2, 4, time = 6))
input_trend[, 1, ] <- trend_exp$conf.lower[2, , ]
input_trend[, 2, ] <- trend_exp$trend[2, , ]
input_trend[, 3, ] <- trend_exp$conf.upper[2, , ]
input_trend[, 4, ] <- trend_exp$trend[1, , ]

VizVsLTime(input_trend,
           toptitle = "Trend of 'tas' over Europe",
           ytitle = "K/year", limits = c(-0.05, 0.1),
           monini = 11, freq = 1,
           listexp = c('SEAS5', 'Meteo-France System 7'), 
           fileout = 'ts_trend_exp1_exp2.png')
```
<img src="./Figures/ts_trend_exp1_exp2.png" width="600" />

We use the ensemble-mean anomaly as well to calculate regression between two
experiments by `s2dv::Regression`. Regression() has the same output elements
like Trend() output, and we also need to reorganize them to have a proper
input for VizVsLTime().

```r
reg_ano <- s2dv::Regression(s2dv::MeanDims(ano$exp, 'ensemble')[, 1, ],
                            s2dv::MeanDims(ano$exp, 'ensemble')[, 2, ],
                            reg_dim = "sdate")

input_reg <- array(dim = c(dat = 1, 4, time = 6))
input_reg[1, 1, ] <- reg_ano$conf.lower[2, ]
input_reg[1, 2, ] <- reg_ano$regression[2, ]
input_reg[1, 3, ] <- reg_ano$conf.upper[2, ]
input_reg[1, 4, ] <- reg_ano$regression[1, ]

VizVsLTime(input_reg,
           toptitle = "Regression of System5c3s and Meteo-France System 7, Europe",
           ytitle = "K/year", sizetit = 0.7,
           monini = 11, freq = 1, leg = FALSE,
           fileout = 'ts_regression_exp1_exp2.png')
```
<img src="./Figures/ts_regression_exp1_exp2.png" width="600" />


#### 2. Spread 

We use the anomalies to calculate the following statstical numbers:
interquartile range, maximum minus minimum, standard deviation,
and median absolute deviation by `s2dv::Spread`, across ensemble members
and start dates.

```r
spread <- s2dv::Spread(ano$exp, compute_dim = c("ensemble", "sdate"))
spread <- lapply(spread, aperm, c(2, 1, 3))

spr_stats <- list('IQR', 'Max.-min', 'SD', 'MAD')
names(spr_stats) <- names(spread)

for (i_plot in seq_len(length(spread))) {
  inputs <- list(spread[[i_plot]], 
                 toptitle = paste0(spr_stats[i_plot], " across members and start dates, Europe"),
                 ytitle = "K", sizetit = 0.7,
                 monini = 11, freq = 1,
                 listexp = c('SEAS5', 'Meteo-France System 7'),
                 fileout = paste0('ts_', names(spr_stats)[i_plot], '_exp1_exp2.png'))

  do.call(VizVsLTime, inputs)
}
```
<img src="./Figures/ts_iqr_exp1_exp2.png" width="600" />
<img src="./Figures/ts_maxmin_exp1_exp2.png" width="600" />
<img src="./Figures/ts_sd_exp1_exp2.png" width="600" />
<img src="./Figures/ts_mad_exp1_exp2.png" width="600" />


#### 3. Correlation, RMSE, and RMSSS

We use s2dv function `Corr()`, `RMS()`, and `RMSSS()` to calculate correlation, 
RMSE (root mean square error), and RMSSS (root mean square error skill score) correspondingly
between ensemble-mean experiments and the observation, then adjust the outputs to fit in
`VizVsLTime()`.

```r
corr_ano <- s2dv::Corr(s2dv::MeanDims(ano$exp, 'ensemble'), 
                       s2dv::MeanDims(ano$obs, 'ensemble'),
                       time_dim = 'sdate', dat_dim = 'dat')

input_cor <- array(dim = c(dat = 2, 4, time = 6))
input_cor[, 1, ] <- corr_ano$conf.lower[, 1, ]
input_cor[, 2, ] <- corr_ano$corr[, 1, ]
input_cor[, 3, ] <- corr_ano$conf.upper[, 1, ]
input_cor[, 4, ] <- corr_ano$p.val[, 1, ]

VizVsLTime(input_cor,
           toptitle = "Time correlation with ERA5, over Europe",
           ytitle = "K", sizetit = 0.7,
           monini = 11, freq = 1,
           listexp = c('SEAS5', 'Meteo-France System 7'),
           fileout = 'ts_corr_exp1_exp2_obs.png')
```
<img src="./Figures/ts_corr_exp1_exp2_obs.png" width="600" />

```r
rms_ano <- s2dv::RMS(s2dv::MeanDims(ano$exp, 'ensemble'), 
                     s2dv::MeanDims(ano$obs, 'ensemble'),
                     time_dim = 'sdate', dat_dim = 'dat')

input_rms <- array(dim = c(dat = 2, 3, time = 6))
input_rms[, 1, ] <- rms_ano$conf.lower[, 1, ]
input_rms[, 2, ] <- rms_ano$rms[, 1, ]
input_rms[, 3, ] <- rms_ano$conf.upper[, 1, ]

VizVsLTime(input_rms,
           toptitle = "RMSE against ERA5, over Europe",
           ytitle = "K", sizetit = 0.7,
           monini = 11, freq = 1,
           listexp = c('SEAS5', 'Meteo-France System 7'),
           fileout = 'ts_rms_exp1_exp2_obs.png')
```
<img src="./Figures/ts_rms_exp1_exp2_obs.png" width="600" />


```r
rmsss <- s2dv::RMSSS(s2dv::MeanDims(ano$exp, 'ensemble'),
                     s2dv::MeanDims(ano$obs, 'ensemble'),
                     dat_dim = 'dat')
input_rmsss <- array(dim = c(nexp = 2, nobs = 1, 4, time = 6))
input_rmsss[, 1, 2, ] <- rmsss$rmsss
input_rmsss[, 1, 4, ] <- rmsss$p.val

VizVsLTime(input_rmsss,
           toptitle = "RMSSS against ERA5 , Europe",
           ytitle = "K", sizetit = 0.7,
           monini = 11, freq = 1, siglev = TRUE,
           listexp = c('SEAS5', 'Meteo-France System 7'),
           fileout = 'ts_rmsss_exp1_exp2_obs.png')
```
<img src="./Figures/ts_rmsss_exp1_exp2_obs.png" width="600" />


#### 4. The ratio of RMSE between two experiments

Another s2dv function `RatioRMS` calculates the ratio of two experiments against
the same observation. P-value is plotted along the ratio.

```r
ratio_rms <- s2dv::RatioRMS(s2dv::MeanDims(ano$exp[, 1, , ], 'ensemble'),
                            s2dv::MeanDims(ano$exp[, 2, , ], 'ensemble'),
                            ano$obs[, 1, 1, ])

input_ratio_rms <- array(dim = c(1, 4, time = 6))
input_ratio_rms[1, 2, ] <- ratio_rms$ratiorms
input_ratio_rms[1, 4, ] <- ratio_rms$p.val

VizVsLTime(input_ratio_rms,
            toptitle = "RMSE Ratio of SEAS5 and Meteo-France System 7 against ERA5, Europe",
            ytitle = "K", sizetit = 0.6,
            monini = 11, freq = 1, siglev = TRUE, leg = FALSE,
            fileout = 'ts_ratiorms_exp1_exp2_obs.png')
```
<img src="./Figures/ts_ratiorms_exp1_exp2_obs.png" width="600" />


#### 5. The ratio between standard deviation and RMSE

To plot the ratio between the standard deviation and RMSE of the experiment, we can use 
s2dv function `RatioSDRMS()`. P-value is plotted along with the ratios.

```r
ratio_sdrms <- s2dv::RatioSDRMS(ano$exp, ano$obs, dat_dim = 'dat', memb_dim = 'ensemble')

input_ratio_sdrms <- array(dim = c(nexp = 2, nobs = 1, 4, time = 6))
input_ratio_sdrms[, 1, 2, ] <- ratio_sdrms$ratio
input_ratio_sdrms[, 1, 4, ] <- ratio_sdrms$p.val

VizVsLTime(input_ratio_sdrms,
           toptitle = "S. dev. over members and s. dates / RMSE, 
           Europe",
           ytitle = "K", sizetit = 0.7,
           monini = 11, freq = 1, siglev = TRUE,
           listexp = c('SEAS5', 'Meteo-France System 7'),
           fileout = 'ts_ratiosdrms_exp1_exp2_obs.png')
```
<img src="./Figures/ts_ratiosdrms_exp1_exp2_obs.png" width="600" />

We can also calculate the ration of SD and RMS for one experiment only but against
two observational data. Since only one observational data is loaded, we create another
one by adding a little disturbance.

```r
# Create another obs by adding random turbulance
obs2 <- abind::abind(ano$obs,
                     ano$obs + rnorm(length(ano$obs), 0, 0.1), 
                     along = 2)
names(dim(obs2)) <- names(dim(ano$obs))

ratio_sdrms <- s2dv::RatioSDRMS(ClimProjDiags::Subset(ano$exp, 'dat', 1, drop = F),
                                obs2,
                                dat_dim = 'dat', memb_dim = 'ensemble')

input_ratio_sdrms <- array(dim = c(nexp = 1, nobs = 2, 4, time = 6))
input_ratio_sdrms[1, , 2, ] <- ratio_sdrms$ratio
input_ratio_sdrms[1, , 4, ] <- ratio_sdrms$p.val

VizVsLTime(input_ratio_sdrms,
           toptitle = "S. dev. over members and s. dates / RMSE, Europe",
           ytitle = "K", sizetit = 0.7,
           monini = 11, freq = 1, siglev = TRUE,
           listexp = c('SEAS5'),
           listobs = c('ERA5', 'ERA5 with random turbulance'),
           fileout = 'ts_ratiosdrms_exp1_obs1_obs2.png')
```
<img src="./Figures/ts_ratiosdrms_exp1_obs1_obs2.png" width="600" />


#### 6. Effective number of independent data

We use s2dv function `Eno()` to get the effective number of independent data.

```r
eno <- s2dv::Eno(s2dv::MeanDims(ano$exp, 'ensemble'), time_dim = 'sdate')

input_eno <- array(dim = c(dat = 2, 4, time = 6))
input_eno[, 2, ] <- eno

VizVsLTime(input_eno,
            toptitle = "Effective n. of independent data, Europe",
            sizetit = 0.7,
            monini = 11, freq = 1, siglev = FALSE, show_conf = FALSE,
            listexp = c('SEAS5', 'Meteo-France System 7'),
            fileout = 'ts_eno_exp1_exp2.png')
``` 
<img src="./Figures/ts_eno_exp1_exp2.png" width="600" />


### Viz2VarsVsLTime

`Viz2VarsVsLTime()` allows to plot two indices or scores on the same plot,
each with its confidence intervals. It accepts as inputs arrays of only 3 dimensions:

```r
  c(n. of experiments, 3/4, n. of forecast times)
```

i.e., it accepts only indices or scores from a set of experimental datasets against 
a single observation.
The usage is similar to `VizVsLTime()`, with one parameter `listvars`to create legend for variables.
We put correlation and RMS from the previous sections in one plot.

```r
Viz2VarsVsLTime(input_cor[, 1:3, ], input_rms,
                toptitle = "Time correlation and RMSE with ERA5, Europe",
                ytitle = "K", sizetit = 0.7,
                monini = 11, freq = 1, limits = c(-0.8, 2.2),
                listexp = c('SEAS5', 'Meteo-France System 7'),
                listvars = c('Corr', 'RMSE'),
                fileout = 'ts_corr_rms_exp1_exp2_obs.png')
```
<img src="./Figures/ts_corr_rms_exp1_exp2_obs.png" width="600" />


### VizACC

`VizACC()` plots the time series of anomaly correlation coefficients. The accepted 
input dimensions are:
```r
  c(n. of experiments, n. of observational datasets, n. of starting dates, n. of forecast times, 4)
```
The last dimension includes the lower limit of the 95% confidence interval, ACC, the upper limit of
the 95% confidence interval, and the 95% significance level.

The ACCs and confidence intervals of all forecast times and starting dates of 
all experiments are plotted. The consecutive start dates are overlapped to make
comparison easier.

All the start dates of an experiment are painted with the same color. 
There are two possible styles controled by the parameter `points`:
 - Drawing curves along forecast times, as in `VizVsLTime()`. In that case 
`fill` can enable filling the area limited by the confidence intervals.
 - Drawing a point for each forecast time, with vertical lines joined to the
extremes of the confidence interval, limited with notches (default).

Besides, `linezero` draws a line at ordinate 0 and `vlines` draws vertical
lines at any specified set of abscissae.

We need to calculate the anomalies (by `s2dv::Ano_CrossValid`) with all the spatial data first,
then use `s2dv::ACC` to calculate the anomaly correlation coefficients. After that, 
adjust the outputs to fit in `VizACC()`.

```r
ano_latlon <- s2dv::Ano_CrossValid(exp, obs, memb = FALSE, dat_dim = c('dat', 'ensemble'), memb_dim = 'ensemble')

acc <- s2dv::ACC(ano_latlon$exp, ano_latlon$obs, 
                 lat = lat, lon = lon,
                 dat_dim = 'dat', memb_dim = 'ensemble')

input_acc <- array(dim = c(dim(acc$acc)[c('nexp', 'nobs', 'sdate', 'time')], 4))
input_acc[, , , , 1] <- acc$conf.lower
input_acc[, , , , 2] <- acc$acc
input_acc[, , , , 3] <- acc$conf.upper
input_acc[, , , , 4] <- acc$p.val

VizACC(input_acc, sdates, 
       toptitle = "Spatial anomaly corr. coeff. with ERA5 over Europe",
       ytitle = "K", sizetit = 0.7, freq = 1, limits = c(-0.8, 1.6),
       legends = c('SEAS5', 'Meteo-France System 7'),
       fileout = 'ts_acc_exp1_exp2_obs.png')
```
<img src="./Figures/ts_acc_exp1_exp2_obs.png" width="600" />


### VizBoxWhisker

WIP.



