---
title: "Adjusting annual weights"
vignette: >
  %\VignetteIndexEntry{Adjusting annual weights}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
knitr:
  opts_chunk: 
    collapse: true
    comment: '#>'
---

Making a monthly or quarterly Lowe index with annual expenditure or revenue
weights requires adjusting these weights so that the implicit annual quantity vector
is used as the fixed basket when making the index with the usual two-step procedure. This
can be done by price updating the annual weights to the base period of the index,
and it serves as a good example of extending the functions in this package.

Let's start by making some annual weights and quarterly indexes for a year.

```{r}
set.seed(54321)

library(piar)

# Make an aggregation structure.
#                  1
#      |-----------|-----------|
#      11          12          13
#  |---+---|   |---+---|   |---+---|
# 111     121 121     122 131     132

pias <- data.frame(
  level1 = rep(1, 12),
  level2 = rep(c(11, 12, 13), each = 4),
  level3 = rep(c(111, 112, 121, 122, 131, 132), each = 2),
  ea = sprintf("B%02d", 1:12),
  weight = 1:12
) |>
  as_aggregation_structure()

pias

# Make elementary indexes over 4 quarters.
elementals <- matrix(
  runif(12 * 4, 0.4, 1.2),
  nrow = 12,
  dimnames = list(sprintf("B%02d", 1:12), paste0("Q", 1:4))
) |>
  as_index()

elementals
```

Adjusting the weights is simple when there are no missing elementary indexes: the weight
for each elementary aggregate is just divided by the average (fixed-base) index
for each quarter.

```{r}
weights(pias) / rowMeans(as.matrix(chain(elementals)))
```

The procedure is more complicated with missing elementary indexes
as reaggregating these indexes with the newly adjusted weights will generally result
in different imputations for the missing elementary indexes, which in turn gives a
different adjustment for the weights. In practice this procedure is done a few times
until the index values converge to a fixed point. The following function shows how
to do this adjustment using the tools in this package.

```{r}
# Function to adjust annual weights.
adjust_weights <- function(
  index,
  pias,
  tol = .Machine$double.eps^0.5,
  max_iter = 100
) {
  adj_pias <- pias
  for (i in seq_len(max_iter)) {
    # Parentally impute missing elementary indexes.
    agg_index <- aggregate(index, adj_pias, na.rm = TRUE, contrib = FALSE)
    elementals <- chain(agg_index[levels(pias)[[nlevels(pias)]]])
    # Compute annual elementary indexes.
    pb <- rowMeans(as.matrix(elementals))
    # Stop if average price-update weights are within tolerance of original
    # weights; adjust otherwise.
    if (max(abs(pb * weights(adj_pias) - weights(pias))) < tol) {
      message(gettextf("Converged after %d iterations", i - 1))
      return(adj_pias)
    } else {
      weights(adj_pias) <- weights(pias) / pb
    }
  }
  warning("weights adjustment did not converge")
  adj_pias
}
```

```{r}
elementals[11:12] <- NA

adjust_weights(elementals, pias)
```
