---
title: "Temperature-Humidity Index @ Kaunas"
output:
flexdashboard::flex_dashboard:
theme: journal
orientation: rows
source_code: embed
vertical_layout: fill
---
```{r setup, include=FALSE}
# Install / load required R libraries
if (!"pacman" %in% rownames(installed.packages())) install.packages("pacman")
pacman::p_load(xts, prophet, TSrepr, plotly, dygraphs)
# Helper function to estimate various forecasting errors
forecastingErrors <- function(realVals,foreVals,inSamp) {
RMSE <- TSrepr::rmse(realVals,foreVals)
MDAE <- TSrepr::mdae(realVals,foreVals)
MASE <- TSrepr::mae(realVals,foreVals) / inSamp
MAPE <- TSrepr::mape(realVals,foreVals)
SMAPE <- TSrepr::smape(realVals,foreVals)
MAAPE <- TSrepr::maape(realVals,foreVals)
data.frame(RMSE,MDAE,MASE,MAPE,SMAPE,MAAPE)
}
# Forecasting horizon length
forecastingHorizon <- 28
# Load prepared xts data
myDataFull <- readRDS('waqi-covid19-airqualitydata.rds')
myDataTrain <- myDataFull[1:(nrow(myDataFull)-forecastingHorizon-1)]
```
```{r backtesting, include=FALSE}
# Denominator for MASE forecasting error calculations
inSampleMAE <- mean(abs(diff(myDataTrain$THI)), na.rm=T)
# Univariate forecasting of temperature for myDataTrain
df <- data.frame(ds=as.Date(time(myDataTrain)), y=as.numeric(myDataTrain[,'temperature']))
tempModel <- prophet(df, yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
myFuture <- make_future_dataframe(tempModel, periods = forecastingHorizon, freq='day')
tempForecast <- predict(tempModel, myFuture)
# Univariate forecasting of humidity for myDataTrain
df <- data.frame(ds=as.Date(time(myDataTrain)), y=as.numeric(myDataTrain[,'humidity']))
humidModel <- prophet(df, yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
myFuture <- make_future_dataframe(humidModel, periods = forecastingHorizon, freq='day')
humidForecast <- predict(humidModel, myFuture)
# Multivariate forecasting of THI for myDataTrain
df <- data.frame(ds=as.Date(time(myDataTrain)), y=as.numeric(myDataTrain[,'THI']),
myDataTrain[,'temperature'], myDataTrain[,'humidity'])
indexModel <- prophet(yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
indexModel <- add_regressor(indexModel, 'temperature')
indexModel <- add_regressor(indexModel, 'humidity')
indexModel <- fit.prophet(indexModel, df)
myFuture <- make_future_dataframe(indexModel, periods = forecastingHorizon, freq='day')
myFuture <- cbind(myFuture, temperature=tempForecast$yhat, humidity=humidForecast$yhat)
indexForecast <- predict(indexModel, myFuture)
# Calculate forecasting errors for THI on testing data (last observations)
tempPred <- tail(tempForecast$yhat, forecastingHorizon)
humidPred <- tail(humidForecast$yhat, forecastingHorizon)
THI_uniformula <- c(rep(NA, 2*forecastingHorizon), 0.8*tempPred + (humidPred/100)*(tempPred-14.4) + 46.4)
THI_multiforec <- c(rep(NA, 2*forecastingHorizon), tail(indexForecast$yhat, forecastingHorizon))
forecastedTHI <- cbind(tail(myDataFull$THI, 3*forecastingHorizon), THI_uniformula, THI_multiforec)
idx <- 1:forecastingHorizon + 2*forecastingHorizon
errorsTable <- rbind(THI_uniformula=forecastingErrors(forecastedTHI[idx,1], forecastedTHI[idx,2], inSampleMAE),
THI_multiforec=forecastingErrors(forecastedTHI[idx,1], forecastedTHI[idx,3], inSampleMAE))
```
```{r forecasting, include=FALSE}
# Univariate forecasting of temperature for myDataFull
df <- data.frame(ds=as.Date(time(myDataFull)), y=as.numeric(myDataFull[,'temperature']))
tempModel <- prophet(df, yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
myFuture <- make_future_dataframe(tempModel, periods = forecastingHorizon, freq='day')
tempForecast <- predict(tempModel, myFuture)
# Univariate forecasting of humidity for myDataFull
df <- data.frame(ds=as.Date(time(myDataFull)), y=as.numeric(myDataFull[,'humidity']))
humidModel <- prophet(df, yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
myFuture <- make_future_dataframe(humidModel, periods = forecastingHorizon, freq='day')
humidForecast <- predict(humidModel, myFuture)
# Multivariate forecasting of THI for myDataFull
df <- data.frame(ds=as.Date(time(myDataFull)), y=as.numeric(myDataFull[,'THI']),
myDataFull[,'temperature'], myDataFull[,'humidity'])
indexModel <- prophet(yearly.seasonality=T, weekly.seasonality=T, daily.seasonality=F)
indexModel <- add_regressor(indexModel, 'temperature')
indexModel <- add_regressor(indexModel, 'humidity')
indexModel <- fit.prophet(indexModel, df)
myFuture <- make_future_dataframe(indexModel, periods = forecastingHorizon, freq='day')
myFuture <- cbind(myFuture, temperature=tempForecast$yhat, humidity=humidForecast$yhat)
indexForecast <- predict(indexModel, myFuture)
```
Backtesting
=======================================================================
Row
-------------------------------------
### **Reality (black) vs. forecast**
```{r plot.xts, fig.width = 10}
gg <- xts::plot.xts(forecastedTHI)
xts::addLegend(legend.names = names(forecastedTHI), lty=1, col=1:ncol(forecastedTHI))
```
Row {data-height=110}
-------------------------------------
### **Table of forecasting errors**
```{r table}
knitr::kable(errorsTable, digits=3)
```
Components
=======================================================================
```{r prophet, fig.show='hide'}
gg <- prophet_plot_components(indexModel, indexForecast)
```
Row {.tabset .tabset-fade}
-------------------------------------
### **Trend component**
```{r prophet1}
ggplotly(gg[[1]])
```
### **Extra regressors**
```{r prophet4}
ggplotly(gg[[4]])
```
Row {.tabset .tabset-fade}
-------------------------------------
### **Yearly seasonality**
```{r prophet3}
ggplotly(gg[[3]])
```
### **Weekly seasonality**
```{r prophet2}
ggplotly(gg[[2]])
```
Forecasts (ggplotly)
=======================================================================
Row
-------------------------------------
### **Temperature**
```{r ggplotly-temp}
ggplotly(plot(tempModel, tempForecast, ylabel='Celsius (°C)', xlabel='Time (daily)'))
```
Row
-------------------------------------
### **Humidity**
```{r ggplotly-humid}
ggplotly(plot(humidModel, humidForecast, ylabel='Percentage (%)', xlabel='Time (daily)'))
```
Row
-------------------------------------
### **Temperature-Humidity Index**
```{r ggplotly-THI}
ggplotly(plot(indexModel, indexForecast, ylabel='THI', xlabel='Time (daily)'))
```
Forecasts (dygraphs)
=======================================================================
```{r dygraphs, include=FALSE}
timeStamps <- tail(time(myDataFull),1) + 1:forecastingHorizon
```
Row
-------------------------------------
### **Temperature (°C)**
```{r dygraphs-temp}
df <- tempForecast[tempForecast$ds %in% timeStamps, c('yhat_lower','yhat','yhat_upper')]
names(df) <- c('Lo.80', 'Point.Forecast', 'Hi.80')
myFore <- xts(df, order.by = timeStamps)
plotData <- cbind(myDataFull[,'temperature'], myFore)
dygraph(plotData, group = "Air-Data") %>%
dySeries(c('temperature'), label = 'Temperature') %>%
dySeries(c("Lo.80", "Point.Forecast", "Hi.80"), label = "Forecast") %>%
dyCrosshair(direction = "vertical")
```
Row
-------------------------------------
### **Humidity (%)**
```{r dygraphs-humid}
df <- humidForecast[humidForecast$ds %in% timeStamps, c('yhat_lower','yhat','yhat_upper')]
names(df) <- c('Lo.80', 'Point.Forecast', 'Hi.80')
myFore <- xts(df, order.by = timeStamps)
plotData <- cbind(myDataFull[,'humidity'], myFore)
dygraph(plotData, group = "Air-Data") %>%
dySeries(c('humidity'), label = 'Humidity') %>%
dySeries(c("Lo.80", "Point.Forecast", "Hi.80"), label = "Forecast") %>%
dyCrosshair(direction = "vertical")
```
Row
-------------------------------------
### **Temperature-Humidity Index**
```{r dygraphs-THI}
df <- indexForecast[indexForecast$ds %in% timeStamps, c('yhat_lower','yhat','yhat_upper')]
names(df) <- c('Lo.80', 'Point.Forecast', 'Hi.80')
myFore <- xts(df, order.by = timeStamps)
plotData <- cbind(myDataFull[,'THI'], myFore)
dygraph(plotData, group = "Air-Data") %>%
dySeries(c('THI'), label = 'THI') %>%
dySeries(c("Lo.80", "Point.Forecast", "Hi.80"), label = "Forecast") %>%
dyRangeSelector() %>% dyCrosshair(direction = "vertical")
```