# co2 data example

## Loading GPFDA package

    library(GPFDA)

## Loading co2 data

In this example, we use a real dataset and apply a customised covariance kernel.

    co2 <- datasets::co2

For visualisation purposes, we will use only the first five years of the sample.

    y <- co2[1:60]
    n <- length(y)
    input <- 5*(1:n)/n

We want to fit a GPR model using the observed data and then make predictions at a \(1000\) equally spaced time points:

    inputNew <- 5*seq(1/n, 1, length.out=1000) # input test for prediction

## Using exponential covariance kernel

We will first fit a GPR model with a linear trend line for the mean function by setting `meanModel='t'`, and a exponential covariance function by setting `Cov='pow.ex'` and `gamma=1`.

    set.seed(789)
    fit1 <- gpr(input=input, response=y, Cov='pow.ex', gamma=1, meanModel='t',
                          nInitCandidates=50, trace=2)

    sapply(fit1$hyper, exp)
    #>  pow.ex.v  pow.ex.w        vv
    #> 3.432e+00 2.444e+00 2.794e-09

Since the noise variance `vv` estimate is extremely low, this fitted model basically interpolates the datapoints:

    plot(fit1, main="exponential kernel - fitted model")

See below the large uncertainty in predictions for test input points:

    pred1 <- gprPredict(train = fit1, inputNew=inputNew)
    plot(pred1, main="exponential kernel - predictions")

These results suggest that the exponential kernel is likely not to be the best choice. A kernel which takes into account a periodic pattern may be wanted.

## Using a customised covariance kernel

To capture the periodic pattern, we use the following covariance function: \[\begin{equation} k(t,t')=v \exp(-w (t-t')^2 - u \sin^2(\pi (t-t'))), \quad v,w,u > 0 \label{cus-cov} \end{equation}\] (see Williams, C. K., & Rasmussen, C. E. (2006). “Gaussian Processes for Machine Learning”, the MIT press).

This is not a standard covariance kernel and is not included in the package. Therefore, we will define it manually.

### Defining a customised covariance kernel

The first step is to define the covariance kernel itself. The name of the kernel must be constituted by the prefix `cov.` and 6 letters ( e.g., `cov.custom`).

Here the C++ function `distMat` is used to calculate distances \(\exp(w)(t-t')^2\). The similar `distMatSq` function is used when `t` and `t'` are identical. See package documentation for more details.

    cov.custom <- function(hyper, input, inputNew=NULL){
      hyper <- lapply(hyper, exp)
      if(is.null(inputNew)){
        inputNew <- as.matrix(input)
      }else{
        inputNew <- as.matrix(inputNew)
      }
      input <- as.matrix(input)
      A1 <- distMat(input=input, inputNew=inputNew, A=as.matrix(hyper$custom.w),
                            power=2)
      sept <- outer(input[,1], inputNew[,1], "-")
      A2 <- hyper$custom.u*(sin(pi*sept))^2
      customCov <- hyper$custom.v*exp(-A1-A2)
      return(customCov)
    }

### Defining the first derivatives

The second step is to define the first derivative of the log-likelihood with respect to each of the hyperparameters of the covariance kernel.

The name of the functions should look like `Dloglik.custom.w`, where the middle affix is the custom defined name, and the last letter (i.e. `w`) names the vector of the hyperparameters.

For details about derivatives, see Shi, J. Q., and Choi, T. (2011), “Gaussian Process Regression Analysis for Functional Data”, CRC Press.

    Dloglik.custom.w <- function(hyper, input, AlphaQ){
      A1 <- distMatSq(input=input, A=as.matrix(exp(hyper$custom.w)), power=2)
      Dcov <- - cov.custom(hyper, input)*A1
      out <- 0.5*sum(diag(AlphaQ%*%Dcov))
      return(out)
    }

    Dloglik.custom.u <- function(hyper, input, AlphaQ){
      sept <- outer(input[,1], input[,1], "-")
      A2 <- exp(hyper$custom.u)*(sin(pi*sept))^2
      Dcov <- - cov.custom(hyper, input)*A2
      out <- 0.5*sum(diag(AlphaQ%*%Dcov))
      return(out)
    }

    Dloglik.custom.v <- function(hyper, input, AlphaQ){
      Dcov <- cov.custom(hyper, input)
      out <- 0.5*sum(diag(AlphaQ%*%Dcov))
      return(out)
    }

### Defining the second derivatives

The third step is to define the second derivatives of the log-likelihood.

    D2custom.w <- function(hyper, input, inv.Q, Alpha.Q){
      Cov <- cov.custom(hyper, input)
      A1 <- distMatSq(input=input, A=as.matrix(exp(hyper$custom.w)), power=2)
      D1cov <- -Cov*A1
      D2cov <- -(D1cov + Cov)*A1
      D2c.w <- D2(D1cov, D2cov, inv.Q, Alpha.Q)
      return(D2c.w)
    }

    D2custom.u <- function(hyper, input, inv.Q, Alpha.Q){
      Cov <- cov.custom(hyper, input)
      sept <- outer(input[,1], input[,1], "-")
      A2 <- exp(hyper$custom.u)*(sin(pi*sept))^2
      D1cov <- - Cov*A2
      D2cov <- -(D1cov + Cov)*A2
      D2c.u <- D2(D1cov, D2cov, inv.Q, Alpha.Q)
      return(D2c.u)
    }

    D2custom.v <- function(hyper, input, inv.Q, Alpha.Q){
      out <- cov.custom(hyper, input)
      return(out)
    }

### Defining a diagonal matrix including the signal variance

Finally, we define a function to calculate a diagonal matrix contanining the variance of the customised covariance function. This is used for making predictions at new input points.

    diag.custom <- function(hyper, input){
      Qstar <- rep(exp(hyper$custom.v), nrow(input))
      return(Qstar)
    }

### Fitting the GPR model with the customised kernel and making predictions

    fit2 <- gpr(input=input, response=y, Cov='custom',
                NewHyper=c('custom.w','custom.u','custom.v'), gamma=2, meanModel='t',
                nInitCandidates=50, trace=2, useGradient = F)

    sapply(fit2$hyper, exp)
    #>   custom.u   custom.v   custom.w         vv
    #>  0.4299023 15.4127823  0.0009464  0.0677307

Note that now the noise variance `vv` estimate is no longer so small. Both the fitted model and the predictions seem much more reasonable for this dataset:

    plot(fit2, main="customised kernel - fitted model")

    #> Predicted values not found, ploting fitted values

    pred2 <- gprPredict(train = fit2, inputNew=inputNew)
    plot(pred2, main="customised kernel - predictions")