tests/testthat/test_rates.r

# START

options("hicp.chatty"=FALSE)


# Function rates() --------------------------------------------------------


# (1) monthly rates

# chronological time period:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2021-12-01","2022-01-01","2022-02-01","2022-03-01","2022-04-01")),
        type="month"),
  100*(c(NA, 100/104, 102/100, 105/102, 107/105)-1)
)

# with NAs:
expect_equal(
  rates(x=c(104,100,102,NA,107),
        t=as.Date(c("2021-12-01","2022-01-01","2022-02-01","2022-03-01","2022-04-01")),
        type="month"),
  100*(c(NA, 100/104, 102/100, NA, NA)-1)
)

# with gaps:
expect_equal(
  rates(x=c(104,100,102,107),
        t=as.Date(c("2021-12-01","2022-01-01","2022-02-01","2022-04-01")),
        type="month"),
  100*(c(NA, 100/104, 102/100, NA)-1)
)

# false time ordering:
expect_equal(
  rates(x=c(100,104,105,102,107),
        t=as.Date(c("2022-01-01","2021-12-01","2022-03-01","2022-02-01","2022-04-01")),
        type="month"),
  100*(c(100/104, NA, 105/102, 102/100, 107/105)-1)
)

# quarterly time frequency:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2022-01-01","2022-04-01","2022-07-01","2022-10-01","2023-01-01")),
        type="month"),
  rep(NA_real_,5)
)

# annual time frequency:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2021-12-01","2022-12-01","2023-12-01","2024-12-01","2025-12-01")),
        type="month"),
  rep(NA_real_, 5)
)

# (2) quarterly rates

# chronological time period:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2022-01-01","2022-04-01","2022-07-01","2022-10-01","2023-01-01")),
        type="quarter"),
  100*(c(NA, 100/104, 102/100, 105/102, 107/105)-1)
)

# with NAs:
expect_equal(
  rates(x=c(104,100,102,NA,107),
        t=as.Date(c("2022-01-01","2022-04-01","2022-07-01","2022-10-01","2023-01-01")),
        type="quarter"),
  100*(c(NA, 100/104, 102/100, NA, NA)-1)
)

# with gaps:
expect_equal(
  rates(x=c(104,100,102,107),
        t=as.Date(c("2022-01-01","2022-04-01","2022-07-01","2023-01-01")),
        type="quarter"),
  100*(c(NA, 100/104, 102/100, NA)-1)
)

# false time ordering:
expect_equal(
  rates(x=c(100,104,105,102,107),
        t=as.Date(c("2022-04-01","2022-01-01","2022-10-01","2022-07-01","2023-01-01")),
        type="quarter"),
  100*(c(100/104, NA, 105/102, 102/100, 107/105)-1)
)

# monthly time frequency:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2021-12-01","2022-01-01","2022-02-01","2022-03-01","2022-04-01")),
        type="quarter"),
  100*(c(NA,NA,NA,105/104,107/100)-1)
)

# annual time frequency:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2021-12-01","2022-12-01","2023-12-01","2024-12-01","2025-12-01")),
        type="quarter"),
  rep(NA_real_, 5)
)

# (3) annual rates

# chronological time period:
expect_equal(
  rates(x=c(104,100,102,105,107),
        t=as.Date(c("2021-12-01","2022-12-01","2023-12-01","2024-12-01","2025-12-01")),
        type="year"),
  100*(c(NA, 100/104, 102/100, 105/102, 107/105)-1)
)

# with NAs:
expect_equal(
  rates(x=c(104,100,102,NA,107),
        t=as.Date(c("2021-12-01","2022-12-01","2023-12-01","2024-12-01","2025-12-01")),
        type="year"),
  100*(c(NA, 100/104, 102/100, NA, NA)-1)
)

# with gaps:
expect_equal(
  rates(x=c(104,100,102,107),
        t=as.Date(c("2021-12-01","2022-12-01","2023-12-01","2025-12-01")),
        type="year"),
  100*(c(NA, 100/104, 102/100, NA)-1)
)

# false time ordering:
expect_equal(
  rates(x=c(100,104,105,102,107),
        t=as.Date(c("2022-12-01","2021-12-01","2024-12-01","2023-12-01","2025-12-01")),
        type="year"),
  100*(c(100/104, NA, 105/102, 102/100, 107/105)-1)
)

# monthly time frequency:
expect_equal(
  rates(x=1:25,
        t=seq.Date(from=as.Date("2019-12-01"), to=as.Date("2021-12-01"), by="1 month"),
        type="year"),
  100*(c(rep(NA,12), (13:25)/(1:13))-1)
)

# quarterly time frequency:
expect_equal(
  rates(x=1:9,
        t=seq.Date(from=as.Date("2019-12-01"), to=as.Date("2021-12-01"), by="3 month"),
        type="year"),
  100*(c(rep(NA,4), (5:9)/(1:5))-1)
)


# Function contrib() ------------------------------------------------------


## check against example in hicp manual

# input:
t <- structure(c(16040, 16071, 16102, 16130, 16161, 16191, 16222,
                 16252, 16283, 16314, 16344, 16375, 16405, 16436, 16467, 16495,
                 16526, 16556, 16587, 16617, 16648, 16679, 16709, 16740, 16770,
                 16801, 16832, 16861, 16892, 16922, 16953, 16983, 17014, 17045,
                 17075, 17106, 17136), class = "Date")
x <- c(108.7, 108.67, 108.74, 108.36, 108.27, 108.2, 108.44, 108.19,
       107.59, 107.75, 106.79, 105.33, 101.85, 98.62, 100.17, 101.91,
       102.01, 102.96, 102.86, 102.14, 99.89, 98.2, 97.68, 97.67, 95.9,
       93.3, 92.07, 93.03, 93.1, 94.64, 96.25, 95.29, 94.29, 95.26,
       96.78, 96.61, 98.35)
x.all <- c(100.11, 98.99, 99.3, 100.23, 100.38, 100.27, 100.38, 99.72,
           99.84, 100.28, 100.22, 100.04, 99.94, 98.4, 99.03, 100.15, 100.39,
           100.61, 100.6, 99.96, 99.97, 100.19, 100.34, 100.19, 100.17,
           98.72, 98.88, 100.11, 100.15, 100.51, 100.68, 100.12, 100.21,
           100.6, 100.85, 100.76, 101.31)
w <- c(NA, 108.07, 108.07, 108.07, 108.07, 108.07, 108.07, 108.07,
       108.07, 108.07, 108.07, 108.07, 108.07, 106.06, 106.06, 106.06,
       106.06, 106.06, 106.06, 106.06, 106.06, 106.06, 106.06, 106.06,
       106.06, 97.4, 97.4, 97.4, 97.4, 97.4, 97.4, 97.4, 97.4, 97.4,
       97.4, 97.4, 97.4)
w.all <- c(NA, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
           1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
           1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
           1000, 1000, 1000, 1000)

# expected results:
ribe.expec <- c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -1.02529813948425,
                -0.866666250168824, -0.640222130696651, -0.619973954901979, -0.515104491704027,
                -0.548704450433906, -0.602525307493953, -0.776522663065849, -0.964384788894606,
                -0.923621450672416, -0.781068214027195, -0.619594501718212, -0.556492789126052,
                -0.84220466625092, -0.916077474262503, -0.917160195487289, -0.857698005505091,
                -0.684618915689184, -0.711747489267791, -0.579213481076454, -0.303897553214695,
                -0.095393755765013, -0.111760293495743, 0.248832116788319)

kirchner.expec <- c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, -1.01703815303061,
                    -0.861586224084169, -0.641540104650217, -0.622734144979741, -0.518601552711475,
                    -0.552332368477231, -0.602460846904482, -0.777862334822627, -0.968075371900026,
                    -0.927715376510117, -0.783441339089728, -0.619594501718212, -0.553088222076527,
                    -0.838171335760115, -0.91752382926534, -0.918835000771934, -0.861111320893986,
                    -0.688028810561959, -0.711825702057316, -0.580058548982564, -0.305078414197261,
                    -0.0964820271246105, -0.112710625635746, 0.248832116788319)

mr.expec <- c(NA, NA, 0.00703817051762693, -0.0380879340850519, -0.00893712532013638, -0.00694071029627297,
              0.023822826922964, -0.0247882510581222, -0.0598855509317528, 0.0159502861616301,
              -0.0952818051681585, -0.144994499148793, -0.346225163991441, -0.336351300932743, 
              0.163933055745136, 0.182857342496143, 0.0103915176817525, 0.098483411797569, 
              -0.0103440065185095, -0.0744842501983668, -0.234253562986313, -0.175932853650891, 
              -0.0540143185777169, -0.00103718406998985, -0.18385643054338, 
              -0.264066736183525, -0.126758761785896, 0.0987735808943386, 0.00711375005741807, 
              0.156439994023563, 0.162965107150613, -0.0970076646685763, -0.101614850500567, 
              0.0984778811211783, 0.153717612572737, -0.0171494834056512, 0.175686792059182)

# chronological time periods:
dt0 <- data.table(t, x, w, x.all, w.all)

expect_equal(
  dt0[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="ribe"))][14:37],
  ribe.expec[14:37]
)

expect_equal(
  dt0[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="kirchner"))][14:37],
  kirchner.expec[14:37]
)

expect_equal(
  dt0[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="month")][3:37],
  mr.expec[3:37]
)

# false time ordering:
dt1 <- copy(dt0)
idx <- sample(1:length(t))
dt1 <- dt1[idx,]

expect_equal(
  dt1[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="ribe"))][order(idx)][14:37],
  ribe.expec[14:37]
)

expect_equal(
  dt1[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="kirchner"))][order(idx)][14:37],
  kirchner.expec[14:37]
)

expect_equal(
  dt1[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="month")][order(idx)][3:37],
  mr.expec[3:37]
)

# with NAs:
dt2 <- copy(dt0)
idx <- c(3,34)
dt2[idx, "x":=NA]

expect_equal(
  dt2[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="ribe"))][14:37],
  ifelse(1:nrow(dt2)%in%c(idx,idx+12), NA, ribe.expec)[14:37]
)

expect_equal(
  dt2[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="kirchner"))][14:37],
  ifelse(1:nrow(dt2)%in%c(idx,idx+12), NA, kirchner.expec)[14:37]
)

expect_equal(
  dt2[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="month")][3:37],
  ifelse(1:nrow(dt2)%in%c(idx,idx+1), NA, mr.expec)[3:37]
)

# with gaps:
dt3 <- copy(dt0)
idx <- c(3,34)
dt3 <- dt3[-idx]

expect_equal(
  dt3[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="ribe"))][14:37],
  replace(x=ribe.expec[!1:nrow(dt2)%in%idx], list=14, values=NA)[14:37]
)

expect_equal(
  dt3[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="year", settings=list(method="kirchner"))][14:37],
  replace(x=kirchner.expec[!1:nrow(dt2)%in%idx], list=14, values=NA)[14:37]
)

expect_equal(
  dt3[, contrib(x=x, w=w, t=t, x.all=x.all, w.all=w.all, type="month")][3:37],
  replace(x=mr.expec[!1:nrow(dt2)%in%idx], list=c(3,33), values=NA)[3:37]
)


## make own example

# example with monthly frequency:
dt <- data.table(
  "time"=rep(seq.Date(from=as.Date("2019-12-01"), to=as.Date("2023-12-01"), by="1 month"), times=3),
  "coicop"=rep(c("011","012","013"), each=49),
  "index"=runif(n=3*49, min=80, max=130))

# adjust weights to sum to 1 in each period:
dt[, "weight":=runif(n=1, min=1, max=30), by=list(year(time), coicop)]
dt[, "weight" := weight/sum(weight), by="time"]

# unchain indices:
dt[, "price_ratio" := unchain(x=index, t=time), by="coicop"]

# aggregate, chain and rebase indices:
dtagg1 <- dt[, list("coicop"="01", "weight"=sum(weight), "price_ratio"=laspeyres(x=price_ratio, w0=weight)), by="time"]
dtagg1[, "index":=chain(x=price_ratio, t=time)]
dtagg1[, "index":=rebase(x=index, t=time, t.ref="2019-12")]

# add all-items hicp:
dt1 <- merge(x=dt,
             y=dtagg1[, list(time,index,weight)],
             by="time", all.x=TRUE, suffixes=c("","_all"))

# compute contributions:
dt1[, "ar" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="year"), by="coicop"]
dt1[, "mr" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="month"), by="coicop"]
dt1[, "qr" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="quarter"), by="coicop"]

# annual rates:
expect_equal(
  dt1[, sum(ar), by="time"]$V1[13:49],
  dtagg1[, rates(index,time,"year")][13:49]
)

# monthly rates:
expect_equal(
  dt1[, sum(mr), by="time"]$V1[2:49],
  dtagg1[, rates(index,time,"month")][2:49]
)

# quarterly rates:
expect_equal(
  dt1[, sum(qr), by="time"]$V1[4:49],
  dtagg1[, rates(index,time,"quarter")][4:49]
)

# example data with quarterly frequency:
dt <- data.table(
  "time"=rep(seq.Date(from=as.Date("2019-12-01"), to=as.Date("2023-12-01"), by="3 months"), times=3),
  "coicop"=rep(c("011","012","013"), each=17),
  "index"=runif(n=3*17, min=80, max=130))

# adjust weights to sum to 1 in each period:
dt[, "weight":=runif(n=1, min=1, max=30), by=list(year(time), coicop)]
dt[, "weight" := weight/sum(weight), by="time"]

# unchain indices:
dt[, "price_ratio" := unchain(x=index, t=time), by="coicop"]

# aggregate, chain and rebase indices:
dtagg1 <- dt[, list("coicop"="01", "weight"=sum(weight), "price_ratio"=laspeyres(x=price_ratio, w0=weight)), by="time"]
dtagg1[, "index":=chain(x=price_ratio, t=time)]
dtagg1[, "index":=rebase(x=index, t=time, t.ref="2019-12")]

# add all-items hicp:
dt1 <- merge(x=dt,
             y=dtagg1[, list(time,index,weight)],
             by="time", all.x=TRUE, suffixes=c("","_all"))

dt1[, "ar" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="year"), by="coicop"]
dt1[, "qr" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="quarter"), by="coicop"]

# annual rates:
expect_equal(
  dt1[, sum(ar), by="time"]$V1[5:17],
  dtagg1[, rates(index,time,"year")][5:17]
)

# quarterly rates:
expect_equal(
  dt1[, sum(qr), by="time"]$V1[2:17],
  dtagg1[, rates(index,time,"quarter")][2:17]
)

# example data with annual frequency:
dt <- data.table(
  "time"=rep(seq.Date(from=as.Date("2019-11-17"), to=as.Date("2023-11-17"), by="12 month"), times=3),
  "coicop"=rep(c("011","012","013"), each=5),
  "weight"=c(runif(n=5, min=0.1, max=0.15), 
             runif(n=5, min=0.5, max=0.6),
             runif(n=5, min=0.3, max=0.35)),
  "index"=c(100,103,102,99,120, 100,104,110,98,125, 100,99,98,98,95)
)

# adjust weights to sum to 1 in each period:
dt[, "weight" := weight/sum(weight), by="time"]

# unchain indices:
dt[, "price_ratio" := unchain(x=index, t=time), by="coicop"]

# aggregate, chain and rebase indices:
dtagg1 <- dt[, list("coicop"="01", "weight"=sum(weight), "price_ratio"=laspeyres(x=price_ratio, w0=weight)), by="time"]
dtagg1[, "index":=chain(x=price_ratio, t=time)]
dtagg1[, "index":=rebase(x=index, t=time, t.ref="2019-11")]

# add all-items hicp:
dt1 <- merge(x=dt,
             y=dtagg1[, list(time,index,weight)],
             by="time", all.x=TRUE, suffixes=c("","_all"))

# ribe decomposition:
dt1[, "ar" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="year"), by="coicop"]

# check results:
expect_equal(
  dt1[, sum(ar), by="time"]$V1[2:5],
  dtagg1[, rates(index,time,"year")][2:5]
)

# adjust all weights proportionally:
af <- 4

# aggregate, chain and rebase indices:
dtagg2 <- dt[, list("coicop"="01", "weight"=sum(weight/af), "price_ratio"=laspeyres(x=price_ratio, w0=weight/af)), by="time"]
dtagg2[, "index":=chain(x=price_ratio, t=time)]
dtagg2[, "index":=rebase(x=index, t=time, t.ref="2019-11")]
# -> only difference is the sum of weights

# add all-items hicp:
dt2 <- merge(x=dt,
             y=dtagg2[, list(time,index,weight)],
             by="time", all.x=TRUE, suffixes=c("","_all"))

# ribe decomposition:
dt2[, "ar" := contrib(x=index, w=weight/af, t=time, x.all=index_all, w.all=weight_all, type="year"), by="coicop"]

# check results:
expect_equal(
  dt2[, sum(ar), by="time"]$V1[2:5],
  dtagg2[, rates(index,time,"year")][2:5]
)

expect_equal(dt1$ar, dt2$ar)


# Comparison to published data --------------------------------------------


### HICP

# import data:
load(test_path("testdata","dta.RData"))
load(test_path("testdata","dtm.RData"))
load(test_path("testdata","dtw.RData"))

## Change rates

# compute monthly, annual and 12-month average rates of change:
dtcomp <- copy(dtm)
dtcomp[, "mr_own":=rates(x=index, t=time, type="month"), by="coicop"]
dtcomp[, "ar_own":=rates(x=index, t=time, type="year"), by="coicop"]
dtcomp[, "index_12mar":=convert(x=index, t=time, type="12mavg"), by="coicop"]
dtcomp[, "12mar_own":=rates(x=index_12mar, t=time, type="year"), by="coicop"]

# compare to published change rates:
expect_equal(0, nrow(dtcomp[!is.na(mr) & abs(mr-mr_own)>0.1,]))
expect_equal(0, nrow(dtcomp[!is.na(ar) & abs(ar-ar_own)>0.1,]))
expect_equal(0, nrow(dtcomp[!is.na(`12mar`) & abs(`12mar`-`12mar_own`)>0.1,]))

# annual average rate of change:
dtcomp <- copy(dta)
dtcomp[, "rate_own":=rates(x=index, t=time, type="year"), by=c("geo","coicop")]

# compare to published change rates:
expect_equal(0, nrow(dtcomp[!is.na(rate) & abs(rate-rate_own)>0.1,]))

## Ribe contributions

# merge price indices and item weights:
dtcomp <- merge(x=dtm, y=dtw, by=c("geo","coicop","year"), all.x=TRUE)

# add all-items hicp:
dtcomp <- merge(x=dtcomp,
                y=dtcomp[coicop=="CP00", list(geo,time,index,weight)],
                by=c("geo","time"), all.x=TRUE, suffixes=c("","_all"))

# ribe decomposition:
dtcomp[, "ribe_own" := contrib(x=index, w=weight, t=time, 
                                  x.all=index_all, w.all=weight_all,
                                  type="year", settings=list(method="ribe")), 
       by=c("geo","coicop")]

# compare to published contributions:
expect_equal(0, nrow(dtcomp[!is.na(ribe) & abs(ribe-ribe_own)>0.1, ]))

### OOHPI

# import data:
load(test_path("testdata","dtooh.RData"))

## Change rates

# compute annual rates of change:
dtcomp <- copy(dtooh)
dtcomp[, "qr_own":=rates(x=index, t=time, type="quarter"), by="expend"]
dtcomp[, "ar_own":=rates(x=index, t=time, type="year"), by="expend"]

# compare to published change rates:
expect_equal(0, nrow(dtcomp[!is.na(qr) & abs(qr-qr_own)>0.1,]))
expect_equal(0, nrow(dtcomp[!is.na(ar) & abs(ar-ar_own)>0.1,]))

## Ribe contributions

# add all-items oohpi:
dttmp <- merge(x=dtcomp,
               y=dtcomp[expend=="TOTAL", list(geo,time,index,weight)],
               by=c("geo","time"), all.x=TRUE, suffixes=c("","_all"))

# ribe decompositions of change rates:
dttmp[, "ar" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="year"), by="expend"]
dttmp[, "qr" := contrib(x=index, w=weight, t=time, x.all=index_all, w.all=weight_all, type="quarter"), by="expend"]

# check sums:
dtcomp <- merge(
  x=dttmp[expend=="TOTAL", list(time,ar_own,qr_own)], 
  y=dttmp[expend=="TOTAL", list("ar_sum"=sum(ar), "qr_sum"=sum(qr)), by="time"], 
  by="time", all.x=TRUE, sort=FALSE)

# compare sum of contributions to total change rate:
expect_equal(0, nrow(dtcomp[!is.na(ar_sum) & abs(ar_own-ar_sum)>0.1, ]))
expect_equal(0, nrow(dtcomp[!is.na(qr_sum) & abs(qr_own-qr_sum)>0.1, ]))

# END

Try the hicp package in your browser

Any scripts or data that you put into this service are public.

hicp documentation built on Aug. 8, 2025, 6:30 p.m.