# Superlative aggregation

All of the examples so far have used a single set of weights to aggregate an index. Although this is by far the most common case, it is not suitable for aggregating a superlative index where there is more than one set of aggregation weights that change every period.

## Aggregating a Paasche index

Let’s start by making a chained Paasche index from 6 elementary indexes over 4 time periods to see how to deal with time-varying aggregation weights. This is similar to the example of building an index across multiple basket in `vignette("multiple-baskets")`, except that the weights change each period.

    library(piar)

    set.seed(12345)

    # Make 6 elementary indexes over 4 time periods.
    elementals <- matrix(c(rep(1, 6), runif(6 * 3, 0.8, 1.2)), nrow = 6) |>
      as_index() |>
      set_levels(paste0("B", 1:6))

    head(elementals)

    #> Period-over-period price index for 6 levels over 4 time periods
    #>       time
    #> levels 1         2         3         4
    #>     B1 1 1.0883616 0.9300382 1.0942740
    #>     B2 1 1.1503093 1.0036897 0.8004546
    #>     B3 1 1.1043929 1.0910821 0.9564813
    #>     B4 1 1.1544498 1.1958948 0.9849979
    #>     B5 1 0.9825924 0.8138142 0.9552576
    #>     B6 1 0.8665487 0.8609494 0.9609941

    # Make aggregation weights over 4 time periods.
    #            1
    #      |-----+-----|
    #      11          12
    #  |---+---|   |---+---|
    #  B1  B2  B3  B4  B5  B6

    weights <- data.frame(
      level1 = 1,
      level2 = rep(11:12, each = 3),
      ea = levels(elementals),
      weights = runif(4 * 6, 100, 200),
      period = rep(1:4, each = 6)
    )

    head(weights)

level1 | level2 | ea | weights | period
--- | --- | --- | --- | ---
1 | 11 | B1 | 117.896 | 1
1 | 11 | B2 | 195.166 | 1
1 | 11 | B3 | 145.373 | 1
1 | 12 | B4 | 132.675 | 1
1 | 12 | B5 | 196.542 | 1
1 | 12 | B6 | 170.748 | 1

The key tools to deal with time-varying aggregation weights are the `stack()` and `unstack()` functions. `stack()` appends a later index series onto an earlier one for the same levels, whereas `unstack()` pulls apart an index series for many periods into a collection of one-period indexes.^[`split()` can also be used for this.] These functions allow the aggregation to be done with a map-reduce.

The first step to making a Paasche index is to unstack the elementary indexes into a list of elementary indexes for each period. (Trying to make the elementary indexes period-by-period can be dangerous when there are missing values.)

    elementals <- unstack(elementals)

This is followed by making a sequence of aggregation structures for each set of weights.

    paasche_pias <- split(
      weights[c("level1", "level2", "ea", "weights")],
      weights[["period"]]
    ) |>
      lapply(as_aggregation_structure)

Computing the Paasche index for each period is now just a case of mapping the `aggregate()` function to each elementary index and aggregation structure, and then reducing the result with the `stack()` function.

    paasche <- Map(
      aggregate,
      elementals,
      paasche_pias,
      na.rm = TRUE,
      include_ea = FALSE,
      order = -1
    ) |>
      Reduce(stack, x = _)

    paasche

    #> Period-over-period price index for 3 levels over 4 time periods
    #>       time
    #> levels 1         2         3         4
    #>     1  1 1.0504784 0.9666398 0.9468934
    #>     11 1 1.1117341 0.9920294 0.9329698
    #>     12 1 0.9898158 0.9447976 0.9647535

## Making a Fisher index

Making a chained Fisher index requires two sets of weights: base-period weights to make the Laspeyres index and current-period weights to make the Paasche index. As with the Paasche index this can be done with a map-reduce, except now by passing two aggregation structures to the `aggregate()` function instead of one.

    laspeyres_pias <- paasche_pias[c(1, 1, 2, 3)]

    fisher <- Map(
      aggregate,
      elementals,
      pias = laspeyres_pias,
      pias2 = paasche_pias,
      na.rm = TRUE,
      include_ea = FALSE
    ) |>
      Reduce(stack, x = _)

    fisher

    #> Period-over-period price index for 3 levels over 4 time periods
    #>       time
    #> levels 1         2         3         4
    #>     1  1 1.0509134 0.9783795 0.9600792
    #>     11 1 1.1157686 1.0007074 0.9557479
    #>     12 1 0.9891911 0.9567760 0.9665552

This gives the same result as calculating the Laspeyres and Paasche indexes individually, and then calculating the Fisher index manually.

    laspeyres <- Map(
      aggregate,
      elementals,
      pias = laspeyres_pias,
      na.rm = TRUE,
      include_ea = FALSE
    ) |>
      Reduce(stack, x = _)

    sqrt(as.matrix(laspeyres) * as.matrix(paasche))

1 | 2 | 3 | 4
--- | --- | --- | ---
1.000 | 1.051 | 0.978 | 0.960
1.000 | 1.116 | 1.001 | 0.956
1.000 | 0.989 | 0.957 | 0.967