10  From Regional to Aggregate Fluctuations

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.0
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(labelled)

The {seasonal} and {timetk} packages are needed in this chapter, for seasonal adjustment by X-13 ARIMA-SEATS. If these packages are not installed:

# Do not run
install.packages("seasonal")
install.packages("timetk")

We will need the theme_paper() function to add our theme to the plots made with ggplot2. This function is defined in the utils.R script.

source("../weatherperu/R/utils.R")

10.1 Synthetic measure of the weather

This section provides a detailed description of the methodology employed to create the synthetic measure of the weather.

We need to load the results from the estimations made in Chapter 8.

load("../R/output/resul_lp.rda")
# Data used in the local projections
load("../data/output/df_lp.rda")

The average price of each crop in the data, denoted hereafter \(p_c\):

average_price_crops <- 
  df |> group_by(product_eng) |> 
  summarise(price_crop = mean(Value_prices, na.rm = TRUE))
average_price_crops
# A tibble: 4 × 2
  product_eng price_crop
  <chr>            <dbl>
1 Cassava          0.522
2 Dent corn        0.698
3 Potato           0.573
4 Rice             0.740

Then, we can put the coefficients in a table, for each crop and each time horizon:

weather_variables <- c(
  "temp_max_dev", "precip_sum_dev"
  )
coefs_weather <- map(
  .x = resul_lp,
  .f = ~ .x$coefs |> 
    filter(name %in% weather_variables) |> 
    select(horizon, name, value)
)
names(coefs_weather) <- map_chr(resul_lp, "crop_name")
coefs_weather <- 
  list_rbind(coefs_weather, names_to = "crop")

Then, we follow a methodology consisting of several key steps.

10.1.1 Step 1: Weather Shock Contribution

The first step involves estimating the contribution of weather shocks at each time period (\(t\)) for the chosen crop (\(c\)) and time horizon (\(h\)). This contribution is determined by considering the weather variables, temperature (\(T_{i,t}\)) and precipitation (\(P_{i,t}\)), and their respective coefficients (\(\beta_{c,h}^{T}\) as well as \(\beta_{c,h}^{P}\)). The weather shock contribution (\(\Gamma_{c,i,t,h}\)) is obtained by multiplying these coefficients with the corresponding weather variables and summing them up:

\[\Gamma_{c,i,t,h} = \beta_{c,h}^{T} T_{i,t-h} + \beta_{c,h}^{P} P_{i,t-h} \tag{10.1}\]

This first step is performed, using two user-defined functions: weather_contrib_crop_h() which computes the contribution of the weather for a single crop and time horizon, and with contrib_weather_crop() which uses the former to compute the contribution of the weather for a single crop, for all horizons.

#' Computes the contribution of the weather for a single crop and time horizon
#'
#' @param lp_crop LP for a specific crop
#' @param h horizon (single value)
#' @param weather_variables vector of names of the weather variables
#' @param ic_conf confidence interval used to determine whether a shock has a
#'   significant effect (default to .95)
weather_contrib_crop_h <- function(lp_crop,
                                   h,
                                   weather_variables,
                                   ic_conf = .95) {
  # The data
  data_lp <- lp_crop$data_lp
  data_lp <- data_lp[[which(names(data_lp) == h)]] |> 
    select(region_id, date, product_eng, !!weather_variables) |> 
    ungroup()
  # The coefficients
  lp_coefs <- 
    lp_crop$coefs |> 
    filter(horizon == !!h, name %in% !!weather_variables) |> 
    mutate(
      value_lb = value - qnorm(1 - ((1 - ic_conf) / 2)) * std,
      value_ub = value + qnorm(1 - ((1 - ic_conf) / 2)) * std
    )
  
  # Keeping the values
  lp_coefs_value <- lp_coefs$value
  # The lower and upper bounds
  lp_coefs_value_lb <- lp_coefs$value_lb
  lp_coefs_value_ub <- lp_coefs$value_ub
  
  data_lp |> 
    nest(.by = c(date, region_id)) |> 
    mutate(
      contribution = map(
        .x = data,
        .f = ~ as.matrix(.x[, weather_variables]) %*% lp_coefs_value |> 
          sum()
      ),
      contribution_lb = map(
        .x = data,
        .f = ~ as.matrix(.x[, weather_variables]) %*% lp_coefs_value_lb |> 
          sum()
      ),
      contribution_ub = map(
        .x = data,
        .f = ~ as.matrix(.x[, weather_variables]) %*% lp_coefs_value_ub |> 
          sum()
      )
    ) |> 
    unnest(cols = c(contribution, contribution_lb, contribution_ub)) |> 
    select(-data) |> 
    mutate(
      significant = (contribution_lb > 0 & contribution_ub > 0) | (contribution_lb < 0 & contribution_ub < 0),
      significant = as.numeric(significant)
    ) |> 
    mutate(date = date + lubridate::period(str_c(!!h, " month")))
}
#' Computes the contribution of the weather for a single crop, for all horizons
#'
#' @param lp_crop LP for a specific crop
#' @param weather_variables vector of names of the weather variables
contrib_weather_crop <- function(lp_crop,
                                 weather_variables) {
  horizons <- unique(lp_crop$coefs$horizon)
  
  map(
    .x = horizons,
    .f = ~weather_contrib_crop_h(
      lp_crop = lp_crop, 
      h = .x, 
      weather_variables = weather_variables
    ) |> 
      mutate(horizon = .x)
  ) |> 
    list_rbind() |> 
    # group_by(date) |> 
    # summarise(value = sum(contribution)) |> 
    mutate(crop = lp_crop$crop_name) |> 
    mutate(contribution_signif = contribution * significant)
}

Let us compute the \(\Gamma_{c,i,t,h}\) for each crop, region, date and horizon:

weather_measure_crop <- 
  map(
    .x = resul_lp,
    .f = ~contrib_weather_crop(
      lp_crop = .x, 
      weather_variables = weather_variables
    )
  ) |> 
  list_rbind()

Let us add the monthly regional selling prices of the crops (df$Value_prices), as well as the average crop-specific prices computed earlier on the whole sample (average_price_crops$price_crop) which will be used as a weight when we will aggregate all crops.

weather_measure_crop <- 
  weather_measure_crop |> 
  left_join(
    df |> select(product_eng, region_id, date, y_new, Value_prices),
    by = c("date", "crop" = "product_eng", "region_id")
  ) |> 
  left_join(
    average_price_crops,
    by = c("crop" = "product_eng")
  )

weather_measure_crop
# A tibble: 106,314 × 12
   date       region_id contribution contribution_lb contribution_ub significant
   <date>     <fct>            <dbl>           <dbl>           <dbl>       <dbl>
 1 2001-01-01 1               0.296           0.364          0.227             1
 2 2001-02-01 1               0.194           0.278          0.110             1
 3 2001-03-01 1               0.142           0.172          0.113             1
 4 2001-04-01 1               0.0655          0.118          0.0131            1
 5 2001-05-01 1               0.0988          0.119          0.0788            1
 6 2001-06-01 1               0.158           0.216          0.101             1
 7 2001-07-01 1               0.0957          0.120          0.0711            1
 8 2001-08-01 1               0.0295          0.0419         0.0170            1
 9 2001-09-01 1               0.126           0.133          0.119             1
10 2001-10-01 1               0.0241          0.0397         0.00849           1
# ℹ 106,304 more rows
# ℹ 6 more variables: horizon <dbl>, crop <chr>, contribution_signif <dbl>,
#   y_new <dbl>, Value_prices <dbl>, price_crop <dbl>

10.1.2 Step 2: Quantity Weights

For each crop and date \(t\), we define some weights for the regional observations using the data observed at horizon \(h=0\), as the sum of the monthly agricultural production over the regions considered in the analysis. The monthly production is expressed in monetary terms, by multiplying the quantities (y_new, i.e., \(y^{raw}\)) by unit price (price_crop, i.e., \(p_c\)).

\[\omega_{c,t} = \sum_{i} y^{\text{raw}}_{c,t,i} \times p_{c}\]

quantity_weight <- 
  weather_measure_crop |> 
  filter(horizon == 0) |> 
  group_by(crop, date, horizon, price_crop) |> 
  summarise(quantity_weight = sum(price_crop * y_new), .groups = "drop") |> 
  select(crop, date, quantity_weight)
quantity_weight
# A tibble: 720 × 3
   crop    date       quantity_weight
   <chr>   <date>               <dbl>
 1 Cassava 2001-01-01          36584.
 2 Cassava 2001-02-01          37428.
 3 Cassava 2001-03-01          42350.
 4 Cassava 2001-04-01          42350.
 5 Cassava 2001-05-01          39290.
 6 Cassava 2001-06-01          33562.
 7 Cassava 2001-07-01          33562.
 8 Cassava 2001-08-01          36918.
 9 Cassava 2001-09-01          34391.
10 Cassava 2001-10-01          34391.
# ℹ 710 more rows

10.1.3 Step 3: Crop-Specific Weather-Adjusted Agricultural Production

For each crop \(c\), at each date \(t\), we compute the weather-adjusted agricultural production, \(y_{c,t}^{\omega}\), as the sum of the significant crop-specific contributions of the weather to the agricultural production. The crop-specific contribution across regions is first aggregated at the national level, using an average of the crop and region-specific contribution of the weather to the monetary equivalence of the agricultural production.

\[y_{c,t}^{\omega} = \sum_{h}\sum_{i} \frac{\mathbb{1}_{\text{signif}_{c,i,t,h}} \times \gamma_{c,i,t,h} \times p_{c} \times y^{\text{raw}}_{c,t,i}}{\text{card}(I_{c,t})},\]

where \(\text{card(I)}\) is the number of regions that produce crop \(c\) at time \(t\). The characteristic function \(\mathbb{1}_{\text{signif}_{c,it,h}}\) is equal to 1 when the contribution is significantly different from 0 (using the 95% confidence intervals of the coefficients \(\beta_{c,h}^{T}\) and \(\beta_{c,h}^{P}\)), and is equal to 0 otherwise.

weather_adjusted_ag <- 
  weather_measure_crop |> 
  # each group: observations across regions, for a crop x date x horizon
  group_by(crop, date, horizon, price_crop) |> 
  # weather-adjusted agricultural production at each horizon
  # y_{c,t,h}^{w}
  summarise(
    y_w = sum(price_crop * contribution_signif *  y_new / n(), na.rm = TRUE),
    .groups = "drop"
  ) |> 
  group_by(crop, date) |> 
  # weather-adjusted agricultural production summed over horizons
  # y_{c,t}^{w}
  summarise(
    y_w = sum(y_w),
    .groups = "drop"
  )

10.1.4 Step 4: Aggregation at the National Level

Then, we aggregate the crop-specific weather-adjusted agricultural production at the national level using the following formula: \[y_{t}^{\omega} = \frac{\sum_{c} y_{c,t}^{\omega}}{\sum_{c}\omega_{c,t}},\]

where \(\omega_{c,t}\) are the quantity weights computed in the second step.

w_df <- 
  weather_adjusted_ag |> 
  left_join(quantity_weight, by = c("crop", "date")) |> 
  group_by(date) |> 
  summarise(
    w = sum(y_w) / sum(quantity_weight),
    .groups = "drop"
  )

10.1.5 Step 5: Detrending

Lastly, we express the national weather-adjusted production as a loss, and take it as a deviation from its trend:

\[ W_{t} = -100 \times (y_{t}^{\omega} - \overline{y_{t}^{\omega}}), \]

where \(y_{t}^{\omega}\) is the trend, obtained with the HP filter.

# Load detrending functions
source("../weatherperu/R/detrending.R")

w_df <- w_df |> 
  mutate(
    w_trend = hp_filter_trend(w, freq = 14400),
    w_dt = - 100 * (w - w_trend),
  )

10.2 Agricultural Production

In the second step in the creation of the synthetic measure of the weater, we computed quantity weights \(\omega_{c,t}\), which correspond to the agricultural production in month \(t\) for crop \(t\), expressed in monetary terms. Let us aggregate these values across crops to obtain a monthly agricultural production: \[y_t^{A} = \sum_{c}\omega_{c,t}\]

We then express these as percentage deviation from their trend computed using the HP filter.

agricultural_output <- 
  quantity_weight |> 
  group_by(date) |> 
  summarise(quantity_weight = sum(quantity_weight), .groups = "drop") |> 
  mutate(
    # Remove seasonality
    q_sa = adj_season_X13(quantity_weight, ymd("2001-01-01")),
    # Extract trend
    q_sa_trend = hp_filter_trend(q_sa, freq = 14400),
    # Percentage dev. from trend
    q = 100 * log(q_sa / q_sa_trend)
  ) |> 
  select(date, q)

10.3 Macroeconomic indicators

The vector of endogenous variables in the estimation, denoted as \(Y_t\), consists of eight variables: \(W_t\), \(RER_t\), \(\pi_t^{a}\), \(\pi_{t}\) , \(X_t\), \(y^A_t\), \(y_t\), and \(r_t\):

  • \(W_t\) represents the aggregate measure of weather-driven agricultural losses defined in Section 10.1, which quantifies the loss in agricultural value added due to weather shocks, expressed as a deviation from its trend. It is the focal variable of interest in this analysis.

  • \(RER_t\) denotes the Real Exchange Rate (RER), which reflects the relative value of the domestic currency against a basket of foreign currencies.

  • \(\pi^a_t\) corresponds to the percentage change of the Food Consumer Price Index (CPIA), which serves as a measure of food inflation.

  • \(\pi_t\) corresponds to the percentage change of the Consumer Price Index (CPI), which serves as a measure of inflation.

  • \(X_t\) denotes Exports.

  • \(y^A_t\) is the Agricultural production, expressed as a percentage deviation from trend, detrended and seasonally adjusted.

  • \(r_t\) is the nominal rate.

  • \(y_t\) represents the Gross Domestic Product (GDP), which serves as a measure of the overall economic activity and output in the country.

To construct the vectors \(Y_t\), we use data from the Central Reserve Bank of Perú.

The Real Exchange Rate (RER) data are obtained using the token PN01259PM, the Food Consumer Price Index (CPIA) data with token PN01336PM, the Consumer Price Index (CPI) data with token PN01270PM, the Exports data with token PN01461BM, the nominal rate with token PN07819NM, and the GDP data with token PN01773AM.

Let us load the dataset of macroeconomic variables (see Chapter 3 for details on the construction of these variables).

load("../data/output/macro/df_macro.rda")

10.4 Merging the Data

The sample period for our analysis covers the time span from January 2003 (2003M1) to December 2015 (2015M12). This period provides a comprehensive view of the relationship between weather-driven agricultural losses and the selected economic indicators in Peru.

df_var <- 
  df_macro |> 
  left_join(
    w_df,
    by = "date"
  ) |> 
  left_join(
    agricultural_output,
    by = "date"
  ) |> 
  mutate(
    w = w_dt
  )
  
variable_names <- c(
  "Agricultural losses" = "w",
  "Real exchange rate" = "rer_dt_sa",
  "Inflation rate (pp)" = "pi",
  "Food inflation rate (pp)" = "pia",
  "Exports (pp)" = "x",
  "Agricultural output (pp)" = "q",
  "GDP (pp)" = "y",
  "Interest rate (pp)" = "r"
)

Let us add labels to the new columns:

df_var <- 
  df_var |>  
  labelled::set_variable_labels(
    w = "Agricultural losses"
  )

Let us divide the all the values (except exports) by 100:

df_var <- 
  df_var |>  
  labelled::set_variable_labels(
    w = "Agricultural losses",
    q = "Agricultural output (pp)"
  ) |> 
  mutate(
    w = w / 100, 
    rer_dt_sa = rer_dt_sa / 100, 
    pi = pi / 100, 
    pia = pia / 100, 
    # x
    q = q / 100, 
    y = y / 100, 
    r = r / 100
  )

Figure Figure 10.1 displays the time series data for the variables included in the vector \(Y\).

ggplot(
    data = df_var |> 
      filter(date >= "2003-01-01") |> 
      select(date, w, rer_dt_sa, pi, pia, x, q, y, r) |> 
      pivot_longer(cols = -date) |> 
      mutate(
        name = factor(
          name,
          levels = variable_names,
          labels = names(variable_names)
        )
      ),
    mapping = aes(x = date, y = value)
  ) +
  geom_line() +
  facet_wrap(~name, scales = "free_y") +
  theme_paper() +
  labs(x = NULL, y = NULL)

Figure 10.1: Time Series of Endogenous Variables for Vector Autoregression (VAR) Analysis in Peru (2003-2015)

Before proceeding to the estimation, there is one final step, which involves converting the data into the ts format.

start_date <- "2003-01-01"
df_var_ts <-
  df_var |> 
  filter(date >= start_date) |> 
  select(
    w, rer_dt_sa, pi, pia, x, q, y, r
  ) |>
  ts(start = c(year(start_date), month(start_date)), freq = 12)

10.5 Estimation

We estimate a VAR(p) model with a constant term but no trend. Let us look how many lags we should use, using the automatic selection method provided by the VARselect() function from {vars}.

info_var_estim <- vars::VARselect(y = df_var_ts, type = "const", lag.max = 6)
info_var_estim
$selection
AIC(n)  HQ(n)  SC(n) FPE(n) 
     2      1      1      2 

$criteria
                   1             2             3             4             5
AIC(n) -7.603145e+01 -7.615114e+01 -7.582013e+01 -7.552401e+01 -7.531801e+01
HQ(n)  -7.544435e+01 -7.504217e+01 -7.418930e+01 -7.337131e+01 -7.264345e+01
SC(n)  -7.458635e+01 -7.342150e+01 -7.180595e+01 -7.022529e+01 -6.873476e+01
FPE(n)  9.560089e-34  8.538415e-34  1.209597e-33  1.681684e-33  2.184452e-33
                   6
AIC(n) -7.530403e+01
HQ(n)  -7.210759e+01
SC(n)  -6.743623e+01
FPE(n)  2.411114e-33

10.5.1 Results

Let us estimate a VAR(2) model with a constant term but no trend.

var_l1 <- vars::VAR(y = df_var_ts, p = 2, type = "const")
summary(var_l1)

VAR Estimation Results:
========================= 
Endogenous variables: w, rer_dt_sa, pi, pia, x, q, y, r 
Deterministic variables: const 
Sample size: 154 
Log Likelihood: 4239.75 
Roots of the characteristic polynomial:
0.9241 0.9241 0.7759 0.7759 0.5856 0.5856 0.4255 0.4056 0.4056 0.4033 0.4033 0.3388 0.3388 0.289 0.289 0.2433
Call:
vars::VAR(y = df_var_ts, p = 2, type = "const")


Estimation results for equation w: 
================================== 
w = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

              Estimate Std. Error t value Pr(>|t|)    
w.l1          1.296250   0.081281  15.948  < 2e-16 ***
rer_dt_sa.l1 -0.041676   0.067336  -0.619   0.5370    
pi.l1         0.009657   0.561098   0.017   0.9863    
pia.l1       -0.143790   0.268666  -0.535   0.5934    
x.l1         -0.001513   0.007170  -0.211   0.8332    
q.l1          0.013159   0.013987   0.941   0.3485    
y.l1         -0.055543   0.065063  -0.854   0.3948    
r.l1          0.234368   0.335788   0.698   0.4864    
w.l2         -0.396698   0.080248  -4.943 2.21e-06 ***
rer_dt_sa.l2 -0.013176   0.067655  -0.195   0.8459    
pi.l2         0.418450   0.559924   0.747   0.4561    
pia.l2       -0.139606   0.274847  -0.508   0.6123    
x.l2         -0.011250   0.007101  -1.584   0.1154    
q.l2          0.003562   0.013995   0.255   0.7995    
y.l2         -0.034530   0.066059  -0.523   0.6020    
r.l2         -0.382698   0.332102  -1.152   0.2512    
const         0.018977   0.010368   1.830   0.0694 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.007486 on 137 degrees of freedom
Multiple R-Squared: 0.9441, Adjusted R-squared: 0.9376 
F-statistic: 144.6 on 16 and 137 DF,  p-value: < 2.2e-16 


Estimation results for equation rer_dt_sa: 
========================================== 
rer_dt_sa = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

              Estimate Std. Error t value Pr(>|t|)    
w.l1          0.067795   0.102000   0.665   0.5074    
rer_dt_sa.l1  1.039632   0.084499  12.303   <2e-16 ***
pi.l1        -0.797316   0.704121  -1.132   0.2595    
pia.l1        0.314261   0.337149   0.932   0.3529    
x.l1          0.016395   0.008998   1.822   0.0706 .  
q.l1         -0.045239   0.017553  -2.577   0.0110 *  
y.l1          0.063572   0.081648   0.779   0.4376    
r.l1         -0.404835   0.421380  -0.961   0.3384    
w.l2          0.013489   0.100703   0.134   0.8936    
rer_dt_sa.l2 -0.213210   0.084900  -2.511   0.0132 *  
pi.l2         0.565512   0.702648   0.805   0.4223    
pia.l2       -0.101261   0.344905  -0.294   0.7695    
x.l2         -0.010725   0.008911  -1.204   0.2308    
q.l2          0.022126   0.017562   1.260   0.2099    
y.l2         -0.001542   0.082897  -0.019   0.9852    
r.l2          0.505704   0.416754   1.213   0.2271    
const        -0.009785   0.013010  -0.752   0.4533    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.009394 on 137 degrees of freedom
Multiple R-Squared: 0.7905, Adjusted R-squared: 0.7661 
F-statistic: 32.32 on 16 and 137 DF,  p-value: < 2.2e-16 


Estimation results for equation pi: 
=================================== 
pi = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

              Estimate Std. Error t value Pr(>|t|)  
w.l1         -0.007378   0.023694  -0.311   0.7560  
rer_dt_sa.l1  0.039688   0.019628   2.022   0.0451 *
pi.l1         0.221611   0.163561   1.355   0.1777  
pia.l1       -0.021457   0.078317  -0.274   0.7845  
x.l1         -0.001377   0.002090  -0.659   0.5111  
q.l1         -0.005637   0.004077  -1.383   0.1690  
y.l1          0.018187   0.018966   0.959   0.3393  
r.l1          0.031599   0.097883   0.323   0.7473  
w.l2         -0.003044   0.023392  -0.130   0.8967  
rer_dt_sa.l2 -0.012936   0.019722  -0.656   0.5130  
pi.l2         0.027509   0.163219   0.169   0.8664  
pia.l2       -0.016920   0.080119  -0.211   0.8331  
x.l2          0.001505   0.002070   0.727   0.4685  
q.l2         -0.002572   0.004080  -0.630   0.5295  
y.l2          0.032645   0.019256   1.695   0.0923 .
r.l2         -0.051968   0.096808  -0.537   0.5923  
const         0.002603   0.003022   0.861   0.3906  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.002182 on 137 degrees of freedom
Multiple R-Squared: 0.2855, Adjusted R-squared: 0.202 
F-statistic: 3.421 on 16 and 137 DF,  p-value: 4.243e-05 


Estimation results for equation pia: 
==================================== 
pia = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

              Estimate Std. Error t value Pr(>|t|)  
w.l1         -0.012600   0.048894  -0.258   0.7970  
rer_dt_sa.l1  0.044207   0.040505   1.091   0.2770  
pi.l1         0.007998   0.337523   0.024   0.9811  
pia.l1        0.199111   0.161613   1.232   0.2201  
x.l1         -0.004546   0.004313  -1.054   0.2937  
q.l1         -0.004064   0.008414  -0.483   0.6299  
y.l1         -0.001673   0.039138  -0.043   0.9660  
r.l1         -0.047800   0.201990  -0.237   0.8133  
w.l2          0.003124   0.048272   0.065   0.9485  
rer_dt_sa.l2  0.001701   0.040697   0.042   0.9667  
pi.l2         0.101852   0.336817   0.302   0.7628  
pia.l2       -0.042550   0.165332  -0.257   0.7973  
x.l2          0.003304   0.004271   0.774   0.4405  
q.l2         -0.008241   0.008419  -0.979   0.3293  
y.l2          0.079453   0.039737   1.999   0.0475 *
r.l2          0.016664   0.199772   0.083   0.9336  
const         0.004945   0.006237   0.793   0.4292  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.004503 on 137 degrees of freedom
Multiple R-Squared: 0.1977, Adjusted R-squared: 0.104 
F-statistic:  2.11 on 16 and 137 DF,  p-value: 0.011 


Estimation results for equation x: 
================================== 
x = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

             Estimate Std. Error t value Pr(>|t|)    
w.l1         -1.18187    0.95445  -1.238   0.2177    
rer_dt_sa.l1 -0.08388    0.79070  -0.106   0.9157    
pi.l1         7.08170    6.58876   1.075   0.2843    
pia.l1       -2.14775    3.15484  -0.681   0.4972    
x.l1          0.17675    0.08420   2.099   0.0376 *  
q.l1         -0.19710    0.16425  -1.200   0.2322    
y.l1          0.50300    0.76401   0.658   0.5114    
r.l1          0.96533    3.94304   0.245   0.8070    
w.l2          0.59439    0.94232   0.631   0.5292    
rer_dt_sa.l2  0.03296    0.79445   0.041   0.9670    
pi.l2         6.86060    6.57498   1.043   0.2986    
pia.l2       -2.22654    3.22743  -0.690   0.4914    
x.l2          0.15228    0.08338   1.826   0.0700 .  
q.l2          0.23924    0.16434   1.456   0.1477    
y.l2          0.24774    0.77570   0.319   0.7499    
r.l2         -3.00040    3.89975  -0.769   0.4430    
const         0.75621    0.12174   6.212 5.88e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.08791 on 137 degrees of freedom
Multiple R-Squared: 0.226,  Adjusted R-squared: 0.1356 
F-statistic: 2.501 on 16 and 137 DF,  p-value: 0.002209 


Estimation results for equation q: 
================================== 
q = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

             Estimate Std. Error t value Pr(>|t|)   
w.l1         -0.41168    0.50363  -0.817  0.41511   
rer_dt_sa.l1 -0.20472    0.41722  -0.491  0.62445   
pi.l1         9.40096    3.47667   2.704  0.00772 **
pia.l1       -5.07639    1.66471  -3.049  0.00275 **
x.l1          0.09526    0.04443   2.144  0.03378 * 
q.l1          0.26657    0.08667   3.076  0.00254 **
y.l1         -0.58340    0.40314  -1.447  0.15014   
r.l1          2.51472    2.08061   1.209  0.22888   
w.l2          0.17202    0.49723   0.346  0.72990   
rer_dt_sa.l2 -0.26822    0.41920  -0.640  0.52335   
pi.l2         0.92836    3.46940   0.268  0.78942   
pia.l2       -1.06201    1.70301  -0.624  0.53392   
x.l2         -0.03617    0.04400  -0.822  0.41242   
q.l2         -0.02692    0.08672  -0.310  0.75674   
y.l2         -0.21175    0.40931  -0.517  0.60576   
r.l2         -1.77086    2.05777  -0.861  0.39098   
const        -0.09680    0.06424  -1.507  0.13414   
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.04638 on 137 degrees of freedom
Multiple R-Squared: 0.283,  Adjusted R-squared: 0.1993 
F-statistic:  3.38 on 16 and 137 DF,  p-value: 5.056e-05 


Estimation results for equation y: 
================================== 
y = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

               Estimate Std. Error t value Pr(>|t|)    
w.l1         -0.0388468  0.1069189  -0.363  0.71692    
rer_dt_sa.l1 -0.0176568  0.0885745  -0.199  0.84229    
pi.l1         0.8862080  0.7380791   1.201  0.23194    
pia.l1        0.1247986  0.3534084   0.353  0.72453    
x.l1         -0.0047356  0.0094319  -0.502  0.61642    
q.l1          0.0001594  0.0183992   0.009  0.99310    
y.l1          0.4144573  0.0855853   4.843 3.41e-06 ***
r.l1          0.2686130  0.4417026   0.608  0.54411    
w.l2          0.0838950  0.1055599   0.795  0.42813    
rer_dt_sa.l2 -0.0199987  0.0889948  -0.225  0.82253    
pi.l2         0.1968076  0.7365354   0.267  0.78971    
pia.l2       -0.1801038  0.3615393  -0.498  0.61917    
x.l2          0.0112578  0.0093404   1.205  0.23017    
q.l2          0.0326655  0.0184093   1.774  0.07822 .  
y.l2          0.2596172  0.0868947   2.988  0.00333 ** 
r.l2         -0.2427361  0.4368532  -0.556  0.57936    
const        -0.0102844  0.0136377  -0.754  0.45207    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.009847 on 137 degrees of freedom
Multiple R-Squared: 0.537,  Adjusted R-squared: 0.483 
F-statistic: 9.933 on 16 and 137 DF,  p-value: 3.605e-16 


Estimation results for equation r: 
================================== 
r = w.l1 + rer_dt_sa.l1 + pi.l1 + pia.l1 + x.l1 + q.l1 + y.l1 + r.l1 + w.l2 + rer_dt_sa.l2 + pi.l2 + pia.l2 + x.l2 + q.l2 + y.l2 + r.l2 + const 

               Estimate Std. Error t value Pr(>|t|)    
w.l1         -3.204e-03  1.796e-02  -0.178   0.8587    
rer_dt_sa.l1  3.860e-03  1.488e-02   0.259   0.7957    
pi.l1         4.371e-02  1.240e-01   0.353   0.7249    
pia.l1       -4.174e-03  5.936e-02  -0.070   0.9440    
x.l1          3.720e-04  1.584e-03   0.235   0.8147    
q.l1         -3.753e-03  3.090e-03  -1.214   0.2267    
y.l1          1.507e-02  1.438e-02   1.048   0.2964    
r.l1          1.439e+00  7.419e-02  19.400  < 2e-16 ***
w.l2          1.212e-02  1.773e-02   0.684   0.4954    
rer_dt_sa.l2 -7.812e-03  1.495e-02  -0.523   0.6021    
pi.l2         2.380e-01  1.237e-01   1.924   0.0564 .  
pia.l2       -2.685e-02  6.073e-02  -0.442   0.6590    
x.l2          1.457e-03  1.569e-03   0.929   0.3547    
q.l2         -9.872e-05  3.092e-03  -0.032   0.9746    
y.l2          2.233e-02  1.460e-02   1.530   0.1283    
r.l2         -4.866e-01  7.338e-02  -6.631 7.08e-10 ***
const        -7.081e-04  2.291e-03  -0.309   0.7577    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


Residual standard error: 0.001654 on 137 degrees of freedom
Multiple R-Squared: 0.9823, Adjusted R-squared: 0.9802 
F-statistic: 475.3 on 16 and 137 DF,  p-value: < 2.2e-16 



Covariance matrix of residuals:
                   w  rer_dt_sa         pi        pia          x          q
w          5.604e-05 -1.400e-05  1.544e-06  2.384e-06 -3.209e-05 -3.039e-05
rer_dt_sa -1.400e-05  8.825e-05 -2.736e-06 -3.886e-06 -3.742e-05  1.107e-06
pi         1.544e-06 -2.736e-06  4.762e-06  8.350e-06 -2.479e-05 -1.319e-05
pia        2.384e-06 -3.886e-06  8.350e-06  2.028e-05 -2.337e-05 -3.024e-05
x         -3.209e-05 -3.742e-05 -2.479e-05 -2.337e-05  7.727e-03  3.878e-04
q         -3.039e-05  1.107e-06 -1.319e-05 -3.024e-05  3.878e-04  2.152e-03
y         -7.657e-06  4.471e-06  2.126e-06  1.571e-06 -7.281e-05 -2.817e-05
r          3.800e-07 -6.227e-08  7.953e-07  1.894e-06 -9.867e-06  2.476e-06
                   y          r
w         -7.657e-06  3.800e-07
rer_dt_sa  4.471e-06 -6.227e-08
pi         2.126e-06  7.953e-07
pia        1.571e-06  1.894e-06
x         -7.281e-05 -9.867e-06
q         -2.817e-05  2.476e-06
y          9.697e-05  3.728e-06
r          3.728e-06  2.736e-06

Correlation matrix of residuals:
                 w rer_dt_sa       pi      pia        x        q        y
w          1.00000 -0.199086  0.09454  0.07073 -0.04876 -0.08753 -0.10387
rer_dt_sa -0.19909  1.000000 -0.13349 -0.09187 -0.04531  0.00254  0.04833
pi         0.09454 -0.133485  1.00000  0.84970 -0.12925 -0.13034  0.09895
pia        0.07073 -0.091870  0.84970  1.00000 -0.05904 -0.14476  0.03543
x         -0.04876 -0.045312 -0.12925 -0.05904  1.00000  0.09510 -0.08411
q         -0.08753  0.002540 -0.13034 -0.14476  0.09510  1.00000 -0.06167
y         -0.10387  0.048329  0.09895  0.03543 -0.08411 -0.06167  1.00000
r          0.03069 -0.004008  0.22035  0.25435 -0.06787  0.03228  0.22888
                  r
w          0.030688
rer_dt_sa -0.004008
pi         0.220348
pia        0.254346
x         -0.067867
q          0.032280
y          0.228879
r          1.000000

10.5.2 Restricted VAR

We impose some restrictions on the VAR:

a <- diag(1, 8)
a[lower.tri(a)] <- NA
a
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    0    0    0    0    0    0    0
[2,]   NA    1    0    0    0    0    0    0
[3,]   NA   NA    1    0    0    0    0    0
[4,]   NA   NA   NA    1    0    0    0    0
[5,]   NA   NA   NA   NA    1    0    0    0
[6,]   NA   NA   NA   NA   NA    1    0    0
[7,]   NA   NA   NA   NA   NA   NA    1    0
[8,]   NA   NA   NA   NA   NA   NA   NA    1
svar_a <- vars::SVAR(var_l1, Amat = a, max.iter = 1000)
Warning in vars::SVAR(var_l1, Amat = a, max.iter = 1000): Convergence not
achieved after 1000 iterations. Convergence value: 3.6100260106392e-05 .

The matrix of the estimated coefficients:

svar_a$A
                   w  rer_dt_sa         pi        pia            x          q
w         1.00000000 0.00000000 0.00000000 0.00000000  0.000000000 0.00000000
rer_dt_sa 0.10815795 1.00000000 0.00000000 0.00000000  0.000000000 0.00000000
pi        0.09386521 0.09491595 1.00000000 0.00000000  0.000000000 0.00000000
pia       0.09213301 0.09526335 0.09038651 1.00000000  0.000000000 0.00000000
x         0.13548464 0.13760590 0.13150723 0.12790193  1.000000000 0.00000000
q         0.11268252 0.08346413 0.10030986 0.11414872 -0.047752511 1.00000000
y         0.08709421 0.07446933 0.08062775 0.08040057  0.009363923 0.02307620
r         0.07634922 0.07416946 0.07764853 0.07589053  0.002399862 0.01186734
                   y r
w         0.00000000 0
rer_dt_sa 0.00000000 0
pi        0.00000000 0
pia       0.00000000 0
x         0.00000000 0
q         0.00000000 0
y         1.00000000 0
r         0.07880776 1
save(svar_a, df_var, file = "../R/output/var_objects.rda")

10.5.3 Impulse Response Functions

Let us focus on the impulse to the weather aggregate cost equation, with 95% confidence bootstrapped error bands (500 runs).

impulse_name <- "w"
nb_runs <- 500

irfs_95 <- vars::irf(
  svar_a, impulse = impulse_name, boot = TRUE, ci = .95, 
  n.ahead = 20, runs = nb_runs
)

And with 68% confidence bootstrapped error bands (500 runs, again).

irfs_68 <- vars::irf(
  svar_a, impulse = impulse_name, boot = TRUE, ci = .68, 
  n.ahead = 20, runs = nb_runs
)

Let us save the results:

save(irfs_95, file = "../R/output/irfs_95.rda")
  save(irfs_68, file = "../R/output/irfs_68.rda")
levels <- variable_names
labels <- names(variable_names)

Now, we can plot the resulting IRFs. Let us prepare the data for the response:

df_irfs <- 
  irfs_95$irf[[impulse_name]] |> 
  as_tibble() |> 
  mutate(horizon = row_number() - 1) |> 
  pivot_longer(cols = -horizon) |> 
  mutate(impulse = !!impulse_name)

df_irfs <- 
  df_irfs |> 
  mutate(
    name = factor(
      name,
      levels = !!levels,
      labels = !!labels
    )
  )

The error bands:

df_irfs_ci <- 
  map(
    .x = list(`95` = irfs_95, `68` = irfs_68),
    .f = function(irf_lvl) {
      map(
        .x = list(
          lower = irf_lvl$Lower[[impulse_name]], 
          mean = irf_lvl$irf[[impulse_name]],
          upper = irf_lvl$Upper[[impulse_name]]),
        .f = ~ .x |> 
          as_tibble() |> 
          mutate(horizon = row_number() - 1) |> 
          pivot_longer(cols = -horizon, values_to = "bound") |> 
          mutate(impulse = !!impulse_name)
      ) |> 
        list_rbind(names_to = "bound_type")
    }
  ) |> 
  list_rbind(names_to = "level") |> 
  pivot_wider(names_from = bound_type, values_from = bound)

df_irfs_ci <- 
  df_irfs_ci |> 
  mutate(
    name = factor(
      name,
      levels = !!levels,
      labels = !!labels),
    level = factor(level, levels = c(68, 95), labels = c("68%", "95%"))
  )

And lastly, the graph.

ggplot() +
  geom_ribbon(
    data = df_irfs_ci,
    mapping = aes(
      x = horizon,
      ymin = lower, ymax = upper,
      fill = level),
    alpha = .3
  ) +
  geom_line(
    data = df_irfs_ci,
    mapping = aes(x = horizon, y = mean),
    colour = "#0072B2"
  ) +
  geom_hline(yintercept = 0, colour = "#444444") +
  labs(x = "Horizon", y = NULL) +
  scale_fill_manual(
    "C.I. level", 
    values = c("68%" = "#0072B2", "95%" = "#56B4E9")) +
  facet_wrap(~name, scales = "free_y") +
  theme_paper() +
  coord_cartesian(xlim = c(0, 20))

Figure 10.2: VAR(2) system response to one standard deviation orthogonal shock to the weather aggregate cost equation