diff --git a/DESCRIPTION b/DESCRIPTION index 010711f..240124a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,26 +1,28 @@ Package: piar Title: Price Index Aggregation -Version: 0.5.0.9029 +Version: 0.5.0.9030 Authors@R: c( person("Steve", "Martin", role = c("aut", "cre", "cph"), email = "stevemartin041@gmail.com", comment = c(ORCID = "0000-0003-2544-9480")) ) Description: Most price indexes are made with a two-step procedure, where - period-over-period elemental indexes are first calculated for a - collection of elemental aggregates at each point in time, and - then aggregated according to a price index aggregation structure. - These indexes can then be chained together to form a time series - that gives the evolution of prices with respect to a fixed base - period. This package contains a collections of functions that - revolve around this work flow, making it easy to build standard - price indexes, and implement the methods described by - Balk (2008, ISBN:978-1-107-40496-0), - von der Lippe (2001, ISBN:3-8246-0638-0), - and the CPI manual (2020, ISBN:978-1-51354-298-0) for bilateral - price indexes. + period-over-period elemental indexes are first calculated for a collection + of elemental aggregates at each point in time, and then aggregated according + to a price index aggregation structure. These indexes can then be chained + together to form a time series that gives the evolution of prices with + respect to a fixed base period. This package contains a collections of + functions that revolve around this work flow, making it easy to build + standard price indexes, and implement the methods described by + Balk (2008, ISBN:978-1-107-40496-0), von der Lippe (2001, + ISBN:3-8246-0638-0), and the CPI manual (2020, ISBN:978-1-51354-298-0) + for bilateral price indexes. Depends: R (>= 4.0) -Imports: stats, utils, gpindex (>= 0.5.0), Matrix (>= 1.5-0) +Imports: + stats, + utils, + gpindex (>= 0.5.0), + Matrix (>= 1.5-0) Suggests: rmarkdown, knitr, diff --git a/NAMESPACE b/NAMESPACE index 53631d7..00e2dfa 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -73,6 +73,7 @@ export("weights<-") export(aggregation_structure) export(as_aggregation_structure) export(as_index) +export(carry_backwards) export(carry_forward) export(chain) export(contrib) diff --git a/NEWS.md b/NEWS.md index 0d0d5f5..199e17d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ - The `[[` method for index objects has been removed as it created unexpected problems for little gain. `as.matrix(index)[[1, 1]]` is a more explicit and flexible way to get the same behavior as `index[[1, 1]]`. -- `aggregation_structure()` now orders the levels of an aggregation according to the order they appear in the data. Previously the levels were ordered lexicographically, except for the elemental aggregates. +- `aggregation_structure()` now orders the levels of an aggregation according to the order they appear in the data. Previously the levels were ordered lexicographically, except for the elemental aggregates. This can affect the order in which index values appear for an aggregate index. There are a number of changes to the way product names are handled when making an index and extracting percent-change contributions. @@ -32,6 +32,8 @@ There are a number of changes to the way product names are handled when making a - The `as.matrix()` method for aggregation structures gains a new argument `sparse`. If `sparse = TRUE` then the aggregation matrix is a sparse, rather than dense, matrix. This option can also be used in the `vcov()` method for aggregate price indexes to improve performance for large indexes. +- Added the `carry_backwards()` function to do carry backwards (as opposed to carry forwards) imputation. + ## Bug fixes - Viewing index objects in the RStudio viewer longer gives an error. diff --git a/R/as_aggregation_structure.R b/R/as_aggregation_structure.R index 52e62b2..f4335a7 100644 --- a/R/as_aggregation_structure.R +++ b/R/as_aggregation_structure.R @@ -17,8 +17,9 @@ #' aggregates. The default is to give each elemental aggregate the same weight. #' @param ... Further arguments passed to or used by methods. #' -#' @returns A price index aggregation structure that inherits from -#' [`piar_aggregation_structure`].. +#' @returns +#' A price index aggregation structure that inherits from +#' [`piar_aggregation_structure`]. #' #' @seealso #' [`as.matrix()`][as.matrix.piar_aggregation_structure] and diff --git a/R/contrib.R b/R/contrib.R index 9f289a6..4204d74 100644 --- a/R/contrib.R +++ b/R/contrib.R @@ -10,9 +10,9 @@ #' #' @returns #' A matrix of percent-change contributions with a column for each -#' `period` and a row for each product for which there are contributions -#' in `level`. Contributions are padded with 0 to fit into a rectangular -#' array when products differ over time. +#' `period` and a row for each product (sorted) for which there are +#' contributions in `level`. Contributions are padded with 0 to fit into a +#' rectangular array when products differ over time. #' #' @examples #' prices <- data.frame( diff --git a/R/impute_prices.R b/R/impute_prices.R index 144c21b..a43f281 100644 --- a/R/impute_prices.R +++ b/R/impute_prices.R @@ -5,7 +5,8 @@ #' The carry forward method replaces a missing price for a product by the price #' for the same product in the previous period. It tends to push an index value #' towards 1, and is usually avoided; see paragraph 6.61 in the CPI manual -#' (2020). +#' (2020). The carry backwards method does the opposite, but this is rarely +#' used in practice. #' #' The shadow price method recursively imputes a missing price by the value of #' the price for the same product in the previous period multiplied by the @@ -40,13 +41,13 @@ #' imputes from elemental indexes only (i.e., not recursively). #' @param weights A numeric vector of weights for the prices in `x` (i.e., #' product weights). The default is to give each price equal weight. -#' @param r1 Order of the price index used to calculate the elemental price -#' indexes: 0 for a geometric index (the default), 1 for an arithmetic index, -#' or -1 for a harmonic index. Other values are possible; see +#' @param r1 Order of the generalized-mean price index used to calculate the +#' elemental price indexes: 0 for a geometric index (the default), 1 for an +#' arithmetic index, or -1 for a harmonic index. Other values are possible; see #' [gpindex::generalized_mean()] for details. -#' @param r2 Order of the price index used to aggregate the elemental price -#' indexes: 0 for a geometric index, 1 for an arithmetic index (the default), -#' or -1 for a harmonic index. Other values are possible; see +#' @param r2 Order of the generalized-mean price index used to aggregate the +#' elemental price indexes: 0 for a geometric index, 1 for an arithmetic index +#' (the default), or -1 for a harmonic index. Other values are possible; see #' [gpindex::generalized_mean()] for details. #' #' @returns @@ -109,8 +110,7 @@ shadow_price <- function(x, period, product, ea, price <- res[[t]] # calculate indexes epr <- elemental_index(price / back_price, - ea = ea[[t]], - weights = w[[t]], na.rm = TRUE, r = r1 + ea = ea[[t]], weights = w[[t]], na.rm = TRUE, r = r1 ) if (!is.null(pias)) { epr <- aggregate(epr, pias, na.rm = TRUE, r = r2) @@ -156,3 +156,10 @@ carry_forward <- function(x, period, product) { attributes(res) <- attributes(x) res } + +#' @rdname impute_prices +#' @export +carry_backwards <- function(x, period, product) { + period <- as.factor(period) + carry_forward(x, factor(period, rev(levels(period))), product) +} diff --git a/man/as_aggregation_structure.Rd b/man/as_aggregation_structure.Rd index 0c6ee8d..048a12d 100644 --- a/man/as_aggregation_structure.Rd +++ b/man/as_aggregation_structure.Rd @@ -28,7 +28,7 @@ aggregates. The default is to give each elemental aggregate the same weight.} } \value{ A price index aggregation structure that inherits from -\code{\link{piar_aggregation_structure}}.. +\code{\link{piar_aggregation_structure}}. } \description{ Coerce an object into an aggregation structure object. diff --git a/man/contrib.Rd b/man/contrib.Rd index 9dc9333..7bd999a 100644 --- a/man/contrib.Rd +++ b/man/contrib.Rd @@ -20,9 +20,9 @@ aggregate index).} } \value{ A matrix of percent-change contributions with a column for each -\code{period} and a row for each product for which there are contributions -in \code{level}. Contributions are padded with 0 to fit into a rectangular -array when products differ over time. +\code{period} and a row for each product (sorted) for which there are +contributions in \code{level}. Contributions are padded with 0 to fit into a +rectangular array when products differ over time. } \description{ Extract a matrix of percent-change contributions from a price index. diff --git a/man/impute_prices.Rd b/man/impute_prices.Rd index ccdd7d8..6549007 100644 --- a/man/impute_prices.Rd +++ b/man/impute_prices.Rd @@ -4,6 +4,7 @@ \alias{impute_prices} \alias{shadow_price} \alias{carry_forward} +\alias{carry_backwards} \title{Impute missing prices} \usage{ shadow_price( @@ -18,6 +19,8 @@ shadow_price( ) carry_forward(x, period, product) + +carry_backwards(x, period, product) } \arguments{ \item{x}{A numeric vector of prices.} @@ -40,14 +43,14 @@ imputes from elemental indexes only (i.e., not recursively).} \item{weights}{A numeric vector of weights for the prices in \code{x} (i.e., product weights). The default is to give each price equal weight.} -\item{r1}{Order of the price index used to calculate the elemental price -indexes: 0 for a geometric index (the default), 1 for an arithmetic index, -or -1 for a harmonic index. Other values are possible; see +\item{r1}{Order of the generalized-mean price index used to calculate the +elemental price indexes: 0 for a geometric index (the default), 1 for an +arithmetic index, or -1 for a harmonic index. Other values are possible; see \code{\link[gpindex:generalized_mean]{gpindex::generalized_mean()}} for details.} -\item{r2}{Order of the price index used to aggregate the elemental price -indexes: 0 for a geometric index, 1 for an arithmetic index (the default), -or -1 for a harmonic index. Other values are possible; see +\item{r2}{Order of the generalized-mean price index used to aggregate the +elemental price indexes: 0 for a geometric index, 1 for an arithmetic index +(the default), or -1 for a harmonic index. Other values are possible; see \code{\link[gpindex:generalized_mean]{gpindex::generalized_mean()}} for details.} } \value{ @@ -60,7 +63,8 @@ Impute missing prices using the carry forward or shadow price method. The carry forward method replaces a missing price for a product by the price for the same product in the previous period. It tends to push an index value towards 1, and is usually avoided; see paragraph 6.61 in the CPI manual -(2020). +(2020). The carry backwards method does the opposite, but this is rarely +used in practice. The shadow price method recursively imputes a missing price by the value of the price for the same product in the previous period multiplied by the diff --git a/tests/Examples/piar-Ex.Rout.save b/tests/Examples/piar-Ex.Rout.save index 6a18a40..e7dadbe 100644 --- a/tests/Examples/piar-Ex.Rout.save +++ b/tests/Examples/piar-Ex.Rout.save @@ -580,7 +580,7 @@ b 2.828427 6.928203 > > ### Name: impute_prices > ### Title: Impute missing prices -> ### Aliases: impute_prices shadow_price carry_forward +> ### Aliases: impute_prices shadow_price carry_forward carry_backwards > > ### ** Examples > diff --git a/tests/testthat/test-impute-prices.R b/tests/testthat/test-impute-prices.R index 1ad3a98..3f83c3a 100644 --- a/tests/testthat/test-impute-prices.R +++ b/tests/testthat/test-impute-prices.R @@ -67,3 +67,10 @@ test_that("jumbling prices does nothing", { sp[jumble] ) }) + +test_that("carrying forward/backwards imputation works", { + expect_equal(carry_forward(c(NA, 1, 2, NA, 3), gl(5, 1), gl(1, 5)), + c(NA, 1, 2, 2, 3)) + expect_equal(carry_backwards(c(NA, 1, 2, NA, 3), gl(5, 1), gl(1, 5)), + c(1, 1, 2, 3, 3)) +}) \ No newline at end of file