Longitudinal Analysis of the Brumandhino Data

Author

Daniel Salnikov

Code for Reproducing Analyses in Section 6

This document contains code and explanations to reproduce the results in Section 6. Most of the source code is not printed, however, it can be easily accessed by looking at the code blocks in the Quarto file.

Construction of UTM-distance ``Gram” Matrix

We provide the code for constructing the ``Gram” kernel matrix, where each entry is given by \(\exp[- \{ \delta_{\mathrm{UTM}} (i, \, j) / (2 \cdot 200 )\}^2 ]\), where \(\delta_{\mathrm{UTM}} (i, \, j)\) is the distance in UTM units between locations \(i\) and \(j\). We begin by plotting the geographical locations superimposed on a map with UTM coordinates on the \(\mathrm{x}\)- and \(\mathrm{y}\)-axis.

Zoom: 18

The resulting network is obtained by fitting the graphical_lasso to the ``Gram” matrix constructed above. We plot the network below.

UTM_coords <- matrix(c(X, Y), nrow = 98, ncol =2)
UTM_dist <- function(x1, x2) {
  a = as.numeric(t(x1 - x2) %*% (x1 - x2))
  return(sqrt(a))
}
UTM_dist_matrix <- matrix(rep(0.0, 98*98), nrow = 98, ncol = 98)
#UTM_dist_matrix <- vapply(1:98, function(i){UTM_dist_inner(UTM_coords[i, ])}, rep(0.0, 98))
for (i in 1:98) {
  UTM_dist_matrix[i, ] = vapply(1:98, function(j){UTM_dist(UTM_coords[i, ], UTM_coords[j, ])}, 0.0)
}
View(UTM_dist_matrix)
mean_dist = median(UTM_dist_matrix)
UTM_kernel = exp(- (UTM_dist_matrix / (2 * 200))^2)
hist(UTM_dist_matrix[UTM_dist_matrix > 0], breaks = 50)

net_mat <- glasso(s = UTM_kernel, rho = 0.12, penalize.diagonal = FALSE)
View(net_mat$wi)
b =net_mat$wi
b[abs(b) > 0.0] = 1
diag(b) = 0
net_result = graph_from_adjacency_matrix(b, 'undirected')
V(net_result)$Xcoord <- X
V(net_result)$Ycoord <- Y
V(net_result)$color <- rep('black', 98)
node_labels <- vapply(1:98, function(i) {return(paste0('(', as.character(i), ')'))}, '')
group_legend_labels <- c(vapply(1:3, function(i) {c(paste0('Location: ', as.character(i), ' & Satellite: 1'), 
                                                    paste0('Location: ', as.character(i), ' & Satellite: 2'))}, c('', '')))
location <- matrix(c(V(net_result)$Xcoord, V(net_result)$Ycoord), ncol = 2)
plot.igraph(net_result, vertex.label='', vertex.label.cex = .5, vertex.color = 'black', edge.width = 0.5, vertex.label.color = 'black', 
            vertex.shape = 'circle', vertex.size = 500, mark.col = c('#ADD5D7', '#B3DBB6', '#FDB06C', '#7871A8', '#AACAF5', '#F6C5E2'),
            mark.groups = list(c(1:10), c(11:20), c(21:37), c(72:86), c(38:60), c(c(61:71), c(87:98))), mark.border = NA,
            layout = location, rescale=FALSE, asp = 0, xlim = range(V(net_result)$Xcoord), ylim = range(V(net_result)$Ycoord))
title(main = "\n Brumadinho Dam Location Network", cex.main=3)
legend("bottomright",
       legend = group_legend_labels,
       pch = 16,
       text.col = 'black',
       pt.cex = 2,
       cex = 2,
       bty = 'o', 
       horiz = FALSE,
       col = c('#ADD5D7', '#B3DBB6', '#FDB06C', '#7871A8', '#AACAF5', '#F6C5E2')
       )

Model Order Selection and Exploratory Analysis

We begin by specifying model order for each group. There are six groups in total, one for each satellite and location pair.

locind_edt <- c(rep(1, 10), rep(2, 10), rep(3, 17), rep(5, 23), rep(6, 11), rep(4, 15), rep(6, 12))
bur_netGNAR <- igraphtoGNAR(net_result)
S1 <- as.matrix(GNARtoigraph(bur_netGNAR))
D = distances(graph_from_adjacency_matrix(S1))
D[!is.finite(D)] <- 0
rmax = max(D)
W_bru_data <- fastCorbit::weights_matrix_cpp(bur_netGNAR, rmax)
# Set community membership values
loc1_sat1 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==1, 1, 0))}, 0)
loc1_sat2 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==2, 1, 0))}, 0)
loc2_sat1 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==3, 1, 0))}, 0)
loc2_sat2 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==4, 1, 0))}, 0)
loc3_sat1 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==5, 1, 0))}, 0)
loc3_sat2 <- vapply(1:98, function(x) {return(ifelse(locind_edt[x]==6, 1, 0))}, 0)

bru_data_community_indicators <- list(loc1_sat1, loc1_sat2, loc2_sat1, loc2_sat2, loc3_sat1, loc3_sat2)
W_bru_data <- fastCorbit::weights_matrix_cpp(bur_netGNAR, rmax)
#bru_data_interactions <- list(c(2), c(1), c(4), c(3), c(6), c(5))
bru_data_interactions <- list(c(2:6), c(1, 3:6), c(1,2, 4:6), c(1, 2, 3, 5, 6), c(1, 2, 3, 4, 6), c(1:5))
alpha_order <- rep(1, 6)
beta_order <- list(c(2), c(2), c(2), c(2), c(3), c(3)) # one for each community

We plot a histogram of the centred longitudinal data and analyse Corbit and R-Corbit plots for verifying model order selection of one-lag and stage-three \(r > 1\).

bru_vts <- vapply(1:98, function(x) {return(100.0 * MXD2[, x] - colMeans(100.0 * MXD2)[x])}, rep(0.0, 44))
hist(recursive_dmat_cols_cpp(lapply(1:43, function(j) {return(as.matrix(bru_vts[2:44, j]))})), breaks = 50, xlab = 'y', main ='Bru VTS')

nacf_raw_data <- corbit_plot_cpp(MXD2, bur_netGNAR, 10, 4, W_bru_data)

max(abs(nacf_raw_data))
[1] 0.2165149
corbit_plot_cpp(MXD2, bur_netGNAR, 10, 4, W_bru_data, partial = "yes")

# Community-wise decomposition for each vector time series
loc1_sat1_nts <- t(vapply(seq(1:44), function(x) {return(loc1_sat1 * MXD2[x, ])}, rep(0.0, 98)))
loc1_sat2_nts <- t(vapply(seq(1:44), function(x) {return(loc1_sat2 * MXD2[x, ])}, rep(0.0, 98)))
loc2_sat1_nts <- t(vapply(seq(1:44), function(x) {return(loc2_sat1 * MXD2[x, ])}, rep(0.0, 98)))
loc2_sat2_nts <- t(vapply(seq(1:44), function(x) {return(loc2_sat2 * MXD2[x, ])}, rep(0.0, 98)))
loc3_sat1_nts <- t(vapply(seq(1:44), function(x) {return(loc3_sat1 * MXD2[x, ])}, rep(0.0, 98)))
loc3_sat2_nts <- t(vapply(seq(1:44), function(x) {return(loc3_sat2 * MXD2[x, ])}, rep(0.0, 98)))


# R-Corbit plots
# NACF
# PNACF
r_corbit_plot_cpp(list(loc1_sat1_nts, loc1_sat2_nts, loc2_sat1_nts, loc2_sat2_nts, loc3_sat1_nts, loc3_sat2_nts), list(bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR),
                  max_lag = 10, max_stage = 4, list(W_bru_data, W_bru_data, W_bru_data, W_bru_data, W_bru_data, W_bru_data), c("loc1_sat1", "loc1_sat2", "loc2_sat1", "loc2_sat2", "loc3_sat1", "loc3_sat2"), 
                  same_net = "no", partial = "no")

r_corbit_plot_cpp(list(loc1_sat1_nts, loc1_sat2_nts, loc2_sat1_nts, loc2_sat2_nts, loc3_sat1_nts, loc3_sat2_nts), list(bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR, bur_netGNAR),
                  max_lag = 5, max_stage = 4, list(W_bru_data, W_bru_data, W_bru_data, W_bru_data, W_bru_data, W_bru_data), c("loc1_sat1", "loc1_sat2", "loc2_sat1", "loc2_sat2", "loc3_sat1", "loc3_sat2"), 
                  same_net = "no", partial = "yes")

Fitting the Dynamic GNAR Model

We proceed to fit each of the models in the paper and compute the corresponding diagnostic statistics. We fill the table one model at a time and plot a histogram of the residuals and their evolution as a linear function of time: \(\hat{u}_{i, t_j} = \hat{b}_{0, i} + \hat{b}_{1, i} \, t_j\) for \(i = 1, \dots, 98\) and \(j = 1, \dots 43\).

Dynamic GNAR Fit

bru_struct_GNARfit <- temp_trend_cv_GNARfit(longitudinal_network_data = 100.0 * MXD2, network = bur_netGNAR, alpha_order = alpha_order, beta_order = beta_order, weight_matrix = W_bru_data, 
                                         covariate_groups = bru_data_community_indicators, interaction_groups = bru_data_interactions, rmax = rmax, iter = 100)

bru_res_interaction_fit <- community_interaction_gnar_fit(bru_struct_GNARfit$`GNAR residuals`, bur_netGNAR, alpha_order, beta_order, W_bru_data, bru_data_community_indicators, 
                                                          bru_data_interactions)
summary(bru_res_interaction_fit)

Call:
lm(formula = yvec ~ . + 0, data = data.frame(full_model, check.names = FALSE))

Residuals:
     Min       1Q   Median       3Q      Max 
-1.75527 -0.15516 -0.00624  0.13564  1.70022 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
alpha.1.1       -0.217627   0.100756  -2.160 0.030834 *  
beta.1.1.1      -0.108612   0.423653  -0.256 0.797679    
beta.1.2.1      -3.535296   2.337807  -1.512 0.130553    
`gamma.1.1.1:2` -0.653016   0.379118  -1.722 0.085061 .  
`gamma.1.2.1:2`  1.071690   1.007424   1.064 0.287485    
`gamma.1.1.1:3`  3.120264   4.812901   0.648 0.516819    
`gamma.1.2.1:3`  1.465068   0.874484   1.675 0.093941 .  
`gamma.1.1.1:4`  1.305999   0.874310   1.494 0.135318    
`gamma.1.2.1:4`  0.853828   0.523373   1.631 0.102883    
`gamma.1.1.1:5` -1.539020   1.119571  -1.375 0.169314    
`gamma.1.2.1:5` -0.223745   0.371615  -0.602 0.547148    
`gamma.1.1.1:6`  0.254417   0.370718   0.686 0.492574    
`gamma.1.2.1:6` -0.084756   0.223129  -0.380 0.704075    
alpha.1.2       -0.138525   0.079776  -1.736 0.082563 .  
beta.1.1.2      -0.149034   0.307010  -0.485 0.627393    
beta.1.2.2      -1.569313   3.432669  -0.457 0.647573    
`gamma.1.1.2:1` -0.608544   0.536524  -1.134 0.256762    
`gamma.1.2.2:1` -1.082090   1.178886  -0.918 0.358729    
`gamma.1.1.2:3`  0.122130   3.039305   0.040 0.967949    
`gamma.1.2.2:3`  1.719757   0.991566   1.734 0.082924 .  
`gamma.1.1.2:4`  0.596137   1.118079   0.533 0.593938    
`gamma.1.2.2:4`  1.054231   0.598440   1.762 0.078206 .  
`gamma.1.1.2:5` -0.674963   0.546612  -1.235 0.216971    
`gamma.1.2.2:5` -0.191122   0.376921  -0.507 0.612139    
`gamma.1.1.2:6` -0.911081   0.751328  -1.213 0.225342    
`gamma.1.2.2:6`  0.345998   0.231953   1.492 0.135861    
alpha.1.3       -0.126933   0.058611  -2.166 0.030393 *  
beta.1.1.3       0.197151   0.206364   0.955 0.339454    
beta.1.2.3      -0.203946   0.376315  -0.542 0.587878    
`gamma.1.1.3:1` -1.756788   4.069953  -0.432 0.666020    
`gamma.1.2.3:1` -0.355103   0.869257  -0.409 0.682918    
`gamma.1.1.3:2`  1.180206   2.301672   0.513 0.608147    
`gamma.1.2.3:2`  1.641600   0.786922   2.086 0.037031 *  
`gamma.1.1.3:4`  0.173343   0.205245   0.845 0.398400    
`gamma.1.2.3:4`  1.177946   0.319651   3.685 0.000232 ***
`gamma.1.1.3:5` -1.273202   0.752295  -1.692 0.090641 .  
`gamma.1.2.3:5` -0.751895   0.389051  -1.933 0.053349 .  
`gamma.1.1.3:6` -0.132016   0.891304  -0.148 0.882259    
`gamma.1.2.3:6` -0.375712   0.259051  -1.450 0.147040    
alpha.1.4       -0.170437   0.039352  -4.331 1.52e-05 ***
beta.1.1.4       0.028643   0.180515   0.159 0.873936    
beta.1.2.4       0.631960   0.341846   1.849 0.064577 .  
`gamma.1.1.4:1` -1.057984   1.014276  -1.043 0.296966    
`gamma.1.2.4:1` -0.566792   0.784789  -0.722 0.470199    
`gamma.1.1.4:2` -0.661051   1.507000  -0.439 0.660936    
`gamma.1.2.4:2`  1.408716   0.772014   1.825 0.068114 .  
`gamma.1.1.4:3`  0.328711   0.234939   1.399 0.161848    
`gamma.1.2.4:3` -1.179271   0.394486  -2.989 0.002812 ** 
`gamma.1.1.4:5` -0.844534   1.650371  -0.512 0.608872    
`gamma.1.2.4:5`  0.574224   0.427860   1.342 0.179643    
`gamma.1.1.4:6`  0.158536   0.462504   0.343 0.731784    
`gamma.1.2.4:6`  0.716913   0.281971   2.543 0.011042 *  
alpha.1.5       -0.294074   0.032088  -9.165  < 2e-16 ***
beta.1.1.5      -0.310992   0.167189  -1.860 0.062939 .  
beta.1.2.5      -0.192966   0.278280  -0.693 0.488083    
beta.1.3.5       0.495713   0.598034   0.829 0.407207    
`gamma.1.1.5:1` -0.187466   1.929726  -0.097 0.922615    
`gamma.1.2.5:1`  0.039132   0.804035   0.049 0.961185    
`gamma.1.3.5:1` -1.797161   0.772484  -2.326 0.020042 *  
`gamma.1.1.5:2`  0.496286   0.991493   0.501 0.616719    
`gamma.1.2.5:2`  0.042224   0.516183   0.082 0.934810    
`gamma.1.3.5:2`  1.399409   0.592714   2.361 0.018271 *  
`gamma.1.1.5:3` -0.313440   0.959233  -0.327 0.743865    
`gamma.1.2.5:3` -0.786476   0.744779  -1.056 0.291037    
`gamma.1.3.5:3`  0.774299   0.257573   3.006 0.002662 ** 
`gamma.1.1.5:4` -0.773193   1.719006  -0.450 0.652885    
`gamma.1.2.5:4`  0.864081   0.636750   1.357 0.174850    
`gamma.1.3.5:4` -0.039980   0.245149  -0.163 0.870461    
`gamma.1.1.5:6`  0.235900   0.127487   1.850 0.064329 .  
`gamma.1.2.5:6` -0.500892   0.186159  -2.691 0.007160 ** 
`gamma.1.3.5:6`  0.189470   0.368161   0.515 0.606833    
alpha.1.6       -0.109822   0.023253  -4.723 2.40e-06 ***
beta.1.1.6      -0.030101   0.123951  -0.243 0.808137    
beta.1.2.6      -0.089611   0.197085  -0.455 0.649362    
beta.1.3.6       1.192041   0.836806   1.425 0.154374    
`gamma.1.1.6:1`  0.471160   1.515372   0.311 0.755877    
`gamma.1.2.6:1`  0.446841   0.847100   0.527 0.597878    
`gamma.1.3.6:1` -1.335547   0.855636  -1.561 0.118628    
`gamma.1.1.6:2` -1.902277   1.114270  -1.707 0.087861 .  
`gamma.1.2.6:2`  0.780845   0.600941   1.299 0.193889    
`gamma.1.3.6:2`  0.887610   0.591148   1.502 0.133303    
`gamma.1.1.6:3` -2.079832   2.456166  -0.847 0.397167    
`gamma.1.2.6:3`  1.018692   0.743198   1.371 0.170547    
`gamma.1.3.6:3`  0.454536   0.247947   1.833 0.066845 .  
`gamma.1.1.6:4` -1.159294   1.060624  -1.093 0.274444    
`gamma.1.2.6:4` -0.941848   0.607556  -1.550 0.121165    
`gamma.1.3.6:4` -0.797861   0.239023  -3.338 0.000851 ***
`gamma.1.1.6:5`  0.005798   0.174871   0.033 0.973553    
`gamma.1.2.6:5`  0.068947   0.267800   0.257 0.796838    
`gamma.1.3.6:5`  0.762579   0.606387   1.258 0.208616    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3078 on 4124 degrees of freedom
Multiple R-squared:  0.08334,   Adjusted R-squared:  0.06334 
F-statistic: 4.166 on 90 and 4124 DF,  p-value: < 2.2e-16
#print(bru_struct_GNARfit$`GNAR parameters`)
print(sum(bru_struct_GNARfit$`GNAR residuals`[2:44, ]^2))
[1] 426.2146
print(sum(bru_struct_GNARfit$`Residuals Vector`^2) / (98 * 43 - 34))
[1] 0.09343159
print(sum(abs(bru_struct_GNARfit$`Residuals Vector`)) / (98 * 43 - 34))
[1] 0.213178
print(sqrt(sum(bru_struct_GNARfit$`GNAR parameters`)^2))
[1] 4.03221
bru_res_interaction_fit_ts <- bru_struct_GNARfit$`Residuals Matrix`


plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(bru_res_interaction_fit_ts[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_res_interaction_fit_ts[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_res_interaction_fit_ts[, j])))
}

l1_dgnar = l1_norm
cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 1.12833482922229
hist(bru_struct_GNARfit$`Residuals Vector`, breaks = 50, xlab = expression(hat(u)), main = "Dynamic GNAR Fit Residuals", cex = 5)

print(GNAR_AIC(bru_struct_GNARfit$`Residuals Vector`, 98, 90))
[1] -2233.23
GNAR_BIC(bru_struct_GNARfit$`Residuals Vector`, 98, 90)
[1] -2229.544
GNAR_HQC(bru_struct_GNARfit$`Residuals Vector`, 98, 90)
[1] -2231.87
nacf_vals <- corbit_plot_cpp(bru_struct_GNARfit$`Residuals Matrix`, bur_netGNAR, 10, 4, W_bru_data)

cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.0381276412373079
nacf_dgnar = max(abs(nacf_vals))

Global-\(\alpha\) GNAR Fit

bru_global_fit <- global_gnar_fit(bru_vts, bur_netGNAR, 1, 3, W_bru_data)
summary(bru_global_fit)

Call:
lm(formula = yvec ~ . + 0, data = data.frame(model_data, check.names = FALSE))

Residuals:
     Min       1Q   Median       3Q      Max 
-2.44405 -0.24554 -0.02023  0.20241  2.87097 

Coefficients:
           Estimate Std. Error t value Pr(>|t|)    
alpha.1.1   0.11887    0.01660   7.159 9.55e-13 ***
beta.1.1.1  0.35687    0.04305   8.290  < 2e-16 ***
beta.1.2.1  0.17732    0.06149   2.884  0.00395 ** 
beta.1.3.1  0.24779    0.05221   4.746 2.15e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3956 on 4210 degrees of freedom
Multiple R-squared:  0.2754,    Adjusted R-squared:  0.2748 
F-statistic: 400.1 on 4 and 4210 DF,  p-value: < 2.2e-16
bru_global_fit_ts = residuals_matrix(bru_global_fit$residuals, 98)
print(sum((bru_global_fit$residuals)^2) / (98 * 43 - 4))
[1] 0.1564628
print(sum(abs(bru_global_fit$residuals)))
[1] 1231.983
print(sqrt(sum((bru_global_fit$coefficients)^2)))
[1] 0.4840794
print(sum(abs(bru_global_fit$coefficients)))
[1] 0.9008567
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(bru_global_fit_ts[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_global_fit_ts[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_global_fit_ts[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 17.0080743279115
l1_gnar = l1_norm

hist(bru_global_fit$residuals, breaks = 50, xlab = expression(hat(u)), main = "Global-alpha GNAR Fit Residuals", cex = 5)

GNAR_AIC(bru_global_fit$residuals, 98, 3)
[1] -2153.53
GNAR_BIC(bru_global_fit$residuals, 98, 3)
[1] -2153.407
GNAR_HQC(bru_global_fit$residuals, 98, 3)
[1] -2153.485
nacf_vals <- corbit_plot_cpp(residuals_matrix(bru_global_fit$residuals, 98), bur_netGNAR, 10, 4, W_bru_data)

cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.0408472856557266
nacf_gnar = max(abs(nacf_vals))

Community-\(\alpha\) with \(\gamma\)-Interactions GNAR Fit

bru_interaction_fit <- community_interaction_gnar_fit(bru_vts, bur_netGNAR, alpha_order, beta_order, W_bru_data, bru_data_community_indicators, bru_data_interactions)
summary(bru_interaction_fit)

Call:
lm(formula = yvec ~ . + 0, data = data.frame(full_model, check.names = FALSE))

Residuals:
    Min      1Q  Median      3Q     Max 
-2.3948 -0.2355 -0.0114  0.1851  2.5294 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
alpha.1.1        0.137919   0.094243   1.463 0.143423    
beta.1.1.1       0.455615   0.374545   1.216 0.223883    
beta.1.2.1      -2.762270   2.307254  -1.197 0.231293    
`gamma.1.1.1:2` -0.471485   0.434907  -1.084 0.278382    
`gamma.1.2.1:2`  0.154660   1.120174   0.138 0.890193    
`gamma.1.1.1:3`  3.173497   4.227396   0.751 0.452877    
`gamma.1.2.1:3`  0.440050   0.826787   0.532 0.594588    
`gamma.1.1.1:4`  0.330297   0.686769   0.481 0.630583    
`gamma.1.2.1:4`  0.838799   0.440635   1.904 0.057030 .  
`gamma.1.1.1:5` -0.359820   0.952880  -0.378 0.705737    
`gamma.1.2.1:5` -0.531165   0.325001  -1.634 0.102262    
`gamma.1.1.1:6`  0.517150   0.381004   1.357 0.174749    
`gamma.1.2.1:6`  0.181722   0.249981   0.727 0.467303    
alpha.1.2       -0.006341   0.091471  -0.069 0.944739    
beta.1.1.2      -0.257374   0.363954  -0.707 0.479507    
beta.1.2.2      -3.018918   4.155262  -0.727 0.467556    
`gamma.1.1.2:1` -0.092411   0.474827  -0.195 0.845699    
`gamma.1.2.2:1`  0.535467   0.946405   0.566 0.571567    
`gamma.1.1.2:3` -0.099799   3.156950  -0.032 0.974783    
`gamma.1.2.2:3`  0.928748   0.960089   0.967 0.333423    
`gamma.1.1.2:4` -0.850276   1.062122  -0.801 0.423442    
`gamma.1.2.2:4`  0.832720   0.478883   1.739 0.082130 .  
`gamma.1.1.2:5` -0.721942   0.549017  -1.315 0.188593    
`gamma.1.2.2:5` -0.141881   0.314771  -0.451 0.652199    
`gamma.1.1.2:6` -0.607897   0.692440  -0.878 0.380046    
`gamma.1.2.2:6`  0.505504   0.246473   2.051 0.040334 *  
alpha.1.3        0.093470   0.062913   1.486 0.137436    
beta.1.1.3       0.232056   0.209966   1.105 0.269135    
beta.1.2.3       0.066101   0.332199   0.199 0.842288    
`gamma.1.1.3:1` -0.563799   4.684016  -0.120 0.904199    
`gamma.1.2.3:1` -0.472984   0.853449  -0.554 0.579470    
`gamma.1.1.3:2`  1.256189   2.857574   0.440 0.660250    
`gamma.1.2.3:2`  1.103262   0.925338   1.192 0.233220    
`gamma.1.1.3:4`  0.340138   0.132843   2.560 0.010489 *  
`gamma.1.2.3:4`  1.111661   0.282800   3.931 8.60e-05 ***
`gamma.1.1.3:5`  0.030928   0.662306   0.047 0.962757    
`gamma.1.2.3:5` -0.089873   0.374894  -0.240 0.810552    
`gamma.1.1.3:6` -0.048243   0.987336  -0.049 0.961031    
`gamma.1.2.3:6`  0.043203   0.296094   0.146 0.883999    
alpha.1.4        0.063552   0.041965   1.514 0.129995    
beta.1.1.4       0.672563   0.156389   4.301 1.74e-05 ***
beta.1.2.4       1.012814   0.287723   3.520 0.000436 ***
`gamma.1.1.4:1` -0.613113   1.007544  -0.609 0.542875    
`gamma.1.2.4:1` -0.448865   0.774918  -0.579 0.562458    
`gamma.1.1.4:2` -0.510754   1.846253  -0.277 0.782068    
`gamma.1.2.4:2`  1.674823   0.906552   1.847 0.064751 .  
`gamma.1.1.4:3`  0.209073   0.220566   0.948 0.343239    
`gamma.1.2.4:3`  0.101526   0.353841   0.287 0.774184    
`gamma.1.1.4:5` -1.300640   1.133887  -1.147 0.251422    
`gamma.1.2.4:5`  1.744354   0.426866   4.086 4.46e-05 ***
`gamma.1.1.4:6`  0.027681   0.552429   0.050 0.960038    
`gamma.1.2.4:6`  1.399633   0.310008   4.515 6.51e-06 ***
alpha.1.5        0.056121   0.032184   1.744 0.081277 .  
beta.1.1.5       0.111035   0.149465   0.743 0.457595    
beta.1.2.5       0.281726   0.237412   1.187 0.235433    
beta.1.3.5       0.869508   0.548096   1.586 0.112722    
`gamma.1.1.5:1`  0.523725   1.917181   0.273 0.784733    
`gamma.1.2.5:1` -2.368492   0.703591  -3.366 0.000769 ***
`gamma.1.3.5:1` -0.213908   0.663471  -0.322 0.747160    
`gamma.1.1.5:2`  2.045783   1.130307   1.810 0.070379 .  
`gamma.1.2.5:2`  1.845753   0.611823   3.017 0.002570 ** 
`gamma.1.3.5:2` -0.061496   0.701557  -0.088 0.930154    
`gamma.1.1.5:3` -0.009463   0.681254  -0.014 0.988918    
`gamma.1.2.5:3` -0.078138   0.593890  -0.132 0.895330    
`gamma.1.3.5:3`  0.173481   0.219821   0.789 0.430045    
`gamma.1.1.5:4`  1.330444   1.466120   0.907 0.364217    
`gamma.1.2.5:4`  0.869355   0.467074   1.861 0.062776 .  
`gamma.1.3.5:4`  0.870340   0.188891   4.608 4.20e-06 ***
`gamma.1.1.5:6`  0.217921   0.142883   1.525 0.127292    
`gamma.1.2.5:6` -0.223395   0.203677  -1.097 0.272789    
`gamma.1.3.5:6`  0.362349   0.404919   0.895 0.370911    
alpha.1.6        0.086177   0.025979   3.317 0.000917 ***
beta.1.1.6      -0.313388   0.138324  -2.266 0.023527 *  
beta.1.2.6       0.289238   0.216964   1.333 0.182567    
beta.1.3.6       0.379524   0.975295   0.389 0.697195    
`gamma.1.1.6:1`  0.129383   1.443176   0.090 0.928568    
`gamma.1.2.6:1`  0.279547   0.741563   0.377 0.706215    
`gamma.1.3.6:1`  0.220300   0.707421   0.311 0.755503    
`gamma.1.1.6:2` -1.928096   1.234860  -1.561 0.118509    
`gamma.1.2.6:2`  2.180667   0.697567   3.126 0.001784 ** 
`gamma.1.3.6:2` -0.697551   0.677962  -1.029 0.303590    
`gamma.1.1.6:3` -0.870495   2.129925  -0.409 0.682783    
`gamma.1.2.6:3`  1.100028   0.549566   2.002 0.045390 *  
`gamma.1.3.6:3`  0.620095   0.216037   2.870 0.004121 ** 
`gamma.1.1.6:4` -3.083422   0.743012  -4.150 3.39e-05 ***
`gamma.1.2.6:4`  0.238954   0.517428   0.462 0.644241    
`gamma.1.3.6:4`  0.427215   0.186291   2.293 0.021883 *  
`gamma.1.1.6:5`  0.479457   0.152078   3.153 0.001629 ** 
`gamma.1.2.6:5`  0.593187   0.240943   2.462 0.013859 *  
`gamma.1.3.6:5`  0.755636   0.545649   1.385 0.166176    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3804 on 4124 degrees of freedom
Multiple R-squared:  0.3438,    Adjusted R-squared:  0.3294 
F-statistic:    24 on 90 and 4124 DF,  p-value: < 2.2e-16
bru_interaction_fit_ts = residuals_matrix(bru_interaction_fit$residuals, 98)
sum((bru_interaction_fit$residuals)^2) / (43 * 98 - 34)
[1] 0.1427297
sum(abs(bru_interaction_fit$residuals))
[1] 1161.581
sqrt(sum((bru_interaction_fit$coefficients)^2))
[1] 9.490969
sum(abs(bru_interaction_fit$coefficients))
[1] 61.36982
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(bru_interaction_fit_ts[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_interaction_fit_ts[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), bru_interaction_fit_ts[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 10.3038446906965
l1_intgnar = l1_norm

hist(bru_interaction_fit$residuals, breaks = 50, xlab = expression(hat(u)), main = "Interactions GNAR Fit Residuals", cex = 5)

GNAR_AIC(bru_interaction_fit$residuals, 98, 0)
[1] -2165.865
GNAR_BIC(bru_interaction_fit$residuals, 98, 30)
[1] -2163.241
GNAR_HQC(bru_interaction_fit$residuals, 98, 30)
[1] -2164.017
nacf_vals <- corbit_plot_cpp(residuals_matrix(bru_interaction_fit$residuals, 98), bur_netGNAR, 10, 4, W_bru_data)

cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.0260958947662737
nacf_intgnar = max(abs(nacf_vals))

Smoother Fit: 98 Independent Splines

mu_hat_mat <- temporal_trend_spline_smoother_cv(spline_residuals = bru_vts[2:44, ], vts_dimension = 98, plag = 1)
mu_res_mat <- bru_vts[2:44, ] - mu_hat_mat[2:44, ]
mu_hat_res <- recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(mu_res_mat[x, ]))}))

sum(mu_hat_res^2) / (98 * 43 - 1)
[1] 0.2386304
sum(abs(mu_hat_res)) / (98 * 43 - 1)
[1] 0.361332
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(mu_res_mat[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), mu_res_mat[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), mu_res_mat[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 43.768348150643
l1_splines = l1_norm

hist(mu_hat_res, breaks = 50, xlab = expression(hat(u)), main = "98 Smoothers Fit Residuals", cex = 5)

GNAR_AIC(mu_hat_res, 98, 0)
[1] -2114.173
GNAR_BIC(mu_hat_res, 98, 30)
[1] -2111.549
GNAR_HQC(mu_hat_res, 98, 30)
[1] -2112.325
nacf_vals <- corbit_plot_cpp(mu_res_mat, bur_netGNAR, 10, 4, W_bru_data)

cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.190624125508691
nacf_splines = max(abs(nacf_vals))

Sparse VAR Fit

sparse_var_fit <- fitVAR(100 * MXD2, p = 1, nlambda = 100)
theta_mat = sparse_var_fit$A[[1]]


sum(abs(sparse_var_fit$residuals))
[1] 1067.933
sparse_var_fit$lambda
[1] 0.0004273116
sparse_var_res <- sparse_var_fit$residuals[2:44, ]

sqrt(sum(theta_mat^2))
[1] 2.816858
b0 = theta_mat
b0[abs(b0) > 0.0] = 1
cat(paste0("Number of non-zero coefficients: ", sum(rowSums(b0))))
Number of non-zero coefficients: 696
sum((sparse_var_res)^2) / (98 * 43 - sum(rowSums(b0)))
[1] 0.1223716
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(sparse_var_res[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), sparse_var_res[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), sparse_var_res[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 9.57597861897959
l1_spvar = l1_norm

hist(recursive_dmat_cols_cpp(lapply(1:98, function(j) {return(as.matrix(sparse_var_fit$residuals[2:44, j]))})), breaks = 50, xlab = 'u', main ='Sparse VAR Fit Res', cex = 5)

GNAR_AIC(recursive_dmat_cols_cpp(lapply(1:98, function(j) {return(as.matrix(sparse_var_fit$residuals[2:44, j]))})), 98, sum(rowSums(b0)))
[1] -2159.198
GNAR_BIC(recursive_dmat_cols_cpp(lapply(1:98, function(j) {return(as.matrix(sparse_var_fit$residuals[2:44, j]))})), 98, sum(rowSums(b0)))
[1] -2130.692
GNAR_HQC(recursive_dmat_cols_cpp(lapply(1:98, function(j) {return(as.matrix(sparse_var_fit$residuals[2:44, j]))})), 98, sum(rowSums(b0)))
[1] -2148.686
nacf_vals <- corbit_plot_cpp(sparse_var_fit$residuals[2:44, ], bur_netGNAR, 10, 4, W_bru_data)

max(abs(nacf_vals))
[1] 0.02236734
cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.0223673381766903
VAR_mat_plot(1, 1, theta_mat)

nacf_var = max(abs(nacf_vals))

Null Model

naive_mean <- bru_vts[2:44, ]
naive_mean_vec <- recursive_dmat_cols_cpp(lapply(2:44, function(x) {return(as.matrix(bru_vts[x, ]))}))
sum(naive_mean_vec^2) / (98 * 43 - 1)
[1] 0.2157901
sum(abs(naive_mean_vec)) / (98 * 43 - 1)
[1] 0.3429551
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(naive_mean[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), naive_mean[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), naive_mean[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 43.978445628209
l1_null = l1_norm

hist(recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(naive_mean[x, ]))})), breaks = 50, xlab = expression(hat(u)), main = "Centred Data", cex = 5)

nacf_vals <- corbit_plot_cpp(bru_vts, bur_netGNAR, 10, 4, W_bru_data)

max(abs(nacf_vals))
[1] 0.2165149
cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.216514916405674
nacf_null = max(abs(nacf_vals))

Naive Model

naive_res <- 100 * t(vapply(2:44, function(x) {MXD2[x, 1:98] - MXD2[x - 1, 1:98]}, rep(0, 98)))
naive_res_vec <- recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(naive_res[x, ]))}))
sum((naive_res)^2) / (98 * 43 - 1)
[1] 0.2645339
sum(abs(naive_res)) / (98 * 43 - 1)
[1] 0.3812074
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(naive_res[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), naive_res[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), naive_res[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 3.80606171851404
l1_naive = l1_norm

hist(recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(naive_res[x, ]))})), breaks = 50, xlab = expression(hat(u)), main = "Naive Fit Residuals", cex = 5)

nacf_vals <- corbit_plot_cpp(naive_res, bur_netGNAR, 10, 4, W_bru_data)

max(abs(nacf_vals))
[1] 0.1772237
cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.177223660548852
nacf_naive = max(abs(nacf_vals))

98 ARs Fits

ar_residuals <- vapply(1:98, function(x){forecast::auto.arima(bru_vts[1:44, x], d = 0, D = 0, max.p = 1, max.q = 0,
                      max.P = 0, max.Q = 0, stationary = TRUE, seasonal = FALSE, ic = 'bic',
                      allowmean = FALSE, allowdrift = FALSE, trace = FALSE)$residuals}, rep(0, 44))[2:44, ]
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
ar_coeffs <- vapply(1:98, function(x){forecast::auto.arima(bru_vts[1:44, x], d = 0, D = 0, max.p = 1, max.q = 0, max.P = 0, max.Q = 0, stationary = TRUE, seasonal = FALSE, ic = 'bic',
                      allowmean = FALSE, allowdrift = FALSE, trace = FALSE)$coef[1]}, 1)

ar_coeffs[is.na(ar_coeffs)] = 1

ar_res_vec <- recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(ar_residuals[x, ]))}))
sum(ar_res_vec^2) / (98 * 42)
[1] 0.1799183
sum(abs(ar_res_vec)) / (98 * 42)
[1] 0.3144478
plot(rep(0, 43), type = 'l', col = 'blue', ylim = c(-2, 2), ylab = expression(hat(u)), xlab = 't', cex = 5)
l1_norm = 0
for (j in 1:98) {
  points(c(1:43), c(ar_residuals[, j]), pch = 16)
  lines(c(1:43), vapply(1:43, function(i) {c(1, i) %*% least_squares_solver(cbind(rep(1, 43), c(1:43)), naive_res[, j])}, 0.0), col = orange_pal[j],
        lty = 2)
  l1_norm = l1_norm + sum(abs(least_squares_solver(cbind(rep(1, 43), c(1:43)), ar_residuals[, j])))
}

cat(paste0("Sum of residual linear trend coefficients: ", as.character(l1_norm)))
Sum of residual linear trend coefficients: 25.5885199670834
l1_ar = l1_norm

hist(recursive_dmat_cols_cpp(lapply(1:43, function(x) {return(as.matrix(ar_residuals[x, ]))})), breaks = 50, xlab = expression(hat(u)), main = "98 ARs Fit Residuals", cex = 5)

nacf_vals <- corbit_plot_cpp(ar_residuals, bur_netGNAR, 10, 4, W_bru_data)

max(abs(nacf_vals))
[1] 0.08554123
cat(paste0("Maximum NACF Value: ", max(abs(nacf_vals))))
Maximum NACF Value: 0.0855412264746516
nacf_ar = max(abs(nacf_vals))

Model Comparisons

The table below reproduces Table 1 and summarizes model comparisons.

MSE MAE NACF Est. Theta Norm Res. Trend Coeff.
Dynamic GNAR 9.0824 20.7229 0.04 4.03 1.13
Global-a GNAR 15.3188 28.6508 0.04 0.90 17.01
g-Interactions GNAR 13.8747 27.0135 0.03 13.73 10.30
98 Smoothers 23.3802 35.4021 0.19 0.00 43.77
Sparse VAR 10.0117 23.7546 0.02 2.82 9.58
98 AR 17.2219 30.0992 0.09 7.56 25.59
Naive Model 25.9182 37.3495 0.18 0.00 3.81
Null Model 21.1424 33.6016 0.22 0.00 43.98

Temporal Trend

We plot the fitted temporal trend, which also accounts for autocorrelation in the data. The plot shows the main effect of this group-wise decreasing trends.

integer(0)

integer(0)

integer(0)

integer(0)

integer(0)

integer(0)