Forecasting
Python is great, but when it comes to forecasting, R still has the upper hand. The Forecast package is the most complete forecasting package available on R or Python, and it’s worth knowing about it.
Here is what we will see in this article: 1. Naive methods 2. Exponential Smoothing (State-space models and DSHW) 3. BATS and TBATS 4. ARIMA/SARIMA models
How to set up a one-step-ahead forecast
For every method, we will build a model on a validation set, forecast with it for the duration of the validation set and compare the forecast with the real observations to obtain a Mean Absolute Percentage Error (MAPE).
library(forecast)
library(MLmetrics)
data=AirPassengers
View(data)
str(data)
#Create samples
training=window(data, start = c(1949,1), end = c(1955,12))
validation=window(data, start = c(1956,1))
1. Naive Methods
Any forecasting method should be evaluated by being compared to a naive method. This helps ensure that the efforts put in having a more complex model are worth it in terms of performance. The simplest of all methods is called simple naive. Extremely simple: the forecast for tomorrow is what we are observing today. Another approach, seasonal naive, is a little more “complex”: the forecast for tomorrow is what we observed the week/month/year (depending what horizon we are working with) before. Here is how to do a seasonal naive forecast:
naive = snaive(training, h=length(validation))
MAPE(naive$mean, validation) * 100
That gives us an MAPE of 27.04%. That’s the score to beat. By the way, remove the s from “snaive” and you have the code for simple naive. Here is how to plot the forecast:
plot(data, col="blue", xlab="Year", ylab="Passengers", main="Seasonal Naive Forecast", type='l')
lines(naive$mean, col="red", lwd=2)
2. Exponential Smoothing
There are many ways to do exponential smoothing. The idea is always to have a declining weight given to observations. The more recent an observation, the more importance it will have in our forecast. Parameters can also be added. You can for instance add a trend paramenter (Holt method) or add a seasonality (Holt-Winters).
2.1 State Space Models
With the Forecast Package, smoothing methods can be placed within the structure of state space models. By using this structure, we can find the optimal exponential smoothing model, using the ets function.
ets_model = ets(training, allow.multiplicative.trend = TRUE)
summary(ets_model)
We see ETS (M, Md, M). This means we have an ets model with multiplicative errors, a multiplicative trend and a multiplicative seasonality. Basically, multiplicative means that the parameter is “amplified” over time. For instance the trend is getting bigger and bigger as time goes by (our case here). Here is how to forecast using the estimated optimal smoothing model:
ets_forecast = forecast(ets_model, h=length(validation))
MAPE(ets_forecast$mean, validation) *100
We see that the upward trend in demand is being capture a little bit (far from perfect, better than naive). It gives an MAPE of 12.6%.
2.2 Double Seasonal Holt-Winters
The ets function is good, but it only allows for one seasonality. Sometimes, the data we have can be composed of multiple seasonalities (monthly and yearly for instance). Double Seasonal Holt-Winters (DSHW) allows for two seasonalities: a smaller one repeated often and a bigger one repeated less often. For the method to work however, the seasonalities need to be nested, meaning one must be an integer multiple of the other (2 and 4, 24 and 168, etc.). The code here is a bit different since we need to specify the lenghts of our two seasonalities (which is not always something we know) and the forecast is computed directly when creating the model with the dshw function.
dshw_model = dshw(training, period1=4, period2 = 12, h=length(validation))
MAPE(dshw_model$mean, validation)*100
We get a MAPE of 3.7% with this method!
3. BATS and TBATS
DSHW is good, but some processes have more complex seasonalities, which our previous functions cannot handle. Indeed, you could have both a weekly and yearly seasonality. You could even have more than 2! BATS and TBATS allow for multiple seasonalities. TBATS is a modification (an improvement really) of BATS that allows for multiple non-integer seasonality cycles. Here is how to build a TBATS model and forecast with it:
tbats_model = tbats(training)
tbats_forecast = forecast(tbats_model, h=length(validation))
MAPE(tbats_forecast$mean, validation) * 100
We get a MAPE of 12.9% for this method.
4. ARIMA/SARIMA models
ARIMA models contain three things: AR(p): autoregressive part of the model. Means that we use p past observations from the timeseries as predictors. Differencing (d): Used to transform the timeseries into a stationary one by taking the differences between successive observations at appropriate lags d. MA(q): uses q past forecast errors as predictors. That’s it for ARIMA but if you know the data you have is seasonal, then you need more. That’s where SARIMA comes into play. SARIMA adds a seasonal part to the model.
The auto.arima function can be used to return the best estimated model. Here is the code:
arima_optimal = auto.arima(training)
The function returned the following model: ARIMA(0,1,1)(1,1,0)[12]. To forecast a SARIMA model (which is what we have here since we have a seasonal part), we can use the sarima.for function from the astsa package.
library(astsa)
sarima_f <- sarima.for(training, n.ahead=length(validation),
p=0,d=1,q=1,P=1,D=1,Q=0,S=12)
MAPE(sarima_f$pred, validation) * 100
We get a MAPE of 6.5% with this SARIMA model.
Just so you know, we could in theory complexify things even more by adding exogenous variables (explanatory variables) to an ARIMA/SARIMA model, which would make it SARIMAX. For this data, DSHW gave the best results. Keep in mind however that no model does best all the time.
LS0tCnRpdGxlOiAiRm9yZWNhc3RpbmciCmF1dGhvcjogIkdhYnJpZWxhIEEwMTYxMjM3MSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCgpgYGAKCgoKIyBGb3JlY2FzdGluZwoKUHl0aG9uIGlzIGdyZWF0LCBidXQgd2hlbiBpdCBjb21lcyB0byBmb3JlY2FzdGluZywgUiBzdGlsbCBoYXMgdGhlIHVwcGVyIGhhbmQuIFRoZSBGb3JlY2FzdCBwYWNrYWdlIGlzIHRoZSBtb3N0IGNvbXBsZXRlIGZvcmVjYXN0aW5nIHBhY2thZ2UgYXZhaWxhYmxlIG9uIFIgb3IgUHl0aG9uLCBhbmQgaXTigJlzIHdvcnRoIGtub3dpbmcgYWJvdXQgaXQuCgpIZXJlIGlzIHdoYXQgd2Ugd2lsbCBzZWUgaW4gdGhpcyBhcnRpY2xlOgoxLiBOYWl2ZSBtZXRob2RzCjIuIEV4cG9uZW50aWFsIFNtb290aGluZyAoU3RhdGUtc3BhY2UgbW9kZWxzIGFuZCBEU0hXKQozLiBCQVRTIGFuZCBUQkFUUwo0LiBBUklNQS9TQVJJTUEgbW9kZWxzCgpIb3cgdG8gc2V0IHVwIGEgb25lLXN0ZXAtYWhlYWQgZm9yZWNhc3QKCkZvciBldmVyeSBtZXRob2QsIHdlIHdpbGwgYnVpbGQgYSBtb2RlbCBvbiBhIHZhbGlkYXRpb24gc2V0LCBmb3JlY2FzdCB3aXRoIGl0IGZvciB0aGUgZHVyYXRpb24gb2YgdGhlIHZhbGlkYXRpb24gc2V0IGFuZCBjb21wYXJlIHRoZSBmb3JlY2FzdCB3aXRoIHRoZSByZWFsIG9ic2VydmF0aW9ucyB0byBvYnRhaW4gYSBNZWFuIEFic29sdXRlIFBlcmNlbnRhZ2UgRXJyb3IgKE1BUEUpLiAKCmBgYHtyfQoKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShNTG1ldHJpY3MpCgpkYXRhPUFpclBhc3NlbmdlcnMKClZpZXcoZGF0YSkKc3RyKGRhdGEpCgojQ3JlYXRlIHNhbXBsZXMKCnRyYWluaW5nPXdpbmRvdyhkYXRhLCBzdGFydCA9IGMoMTk0OSwxKSwgZW5kID0gYygxOTU1LDEyKSkKdmFsaWRhdGlvbj13aW5kb3coZGF0YSwgc3RhcnQgPSBjKDE5NTYsMSkpCgpgYGAKCiMgMS4gTmFpdmUgTWV0aG9kcwoKQW55IGZvcmVjYXN0aW5nIG1ldGhvZCBzaG91bGQgYmUgZXZhbHVhdGVkIGJ5IGJlaW5nIGNvbXBhcmVkIHRvIGEgbmFpdmUgbWV0aG9kLiBUaGlzIGhlbHBzIGVuc3VyZSB0aGF0IHRoZSBlZmZvcnRzIHB1dCBpbiBoYXZpbmcgYSBtb3JlIGNvbXBsZXggbW9kZWwgYXJlIHdvcnRoIGl0IGluIHRlcm1zIG9mIHBlcmZvcm1hbmNlLgpUaGUgc2ltcGxlc3Qgb2YgYWxsIG1ldGhvZHMgaXMgY2FsbGVkIHNpbXBsZSBuYWl2ZS4gRXh0cmVtZWx5IHNpbXBsZTogdGhlIGZvcmVjYXN0IGZvciB0b21vcnJvdyBpcyB3aGF0IHdlIGFyZSBvYnNlcnZpbmcgdG9kYXkuCkFub3RoZXIgYXBwcm9hY2gsIHNlYXNvbmFsIG5haXZlLCBpcyBhIGxpdHRsZSBtb3JlIOKAnGNvbXBsZXjigJ06IHRoZSBmb3JlY2FzdCBmb3IgdG9tb3Jyb3cgaXMgd2hhdCB3ZSBvYnNlcnZlZCB0aGUgd2Vlay9tb250aC95ZWFyIChkZXBlbmRpbmcgd2hhdCBob3Jpem9uIHdlIGFyZSB3b3JraW5nIHdpdGgpIGJlZm9yZS4KSGVyZSBpcyBob3cgdG8gZG8gYSBzZWFzb25hbCBuYWl2ZSBmb3JlY2FzdDoKCmBgYHtyfQoKbmFpdmUgPSBzbmFpdmUodHJhaW5pbmcsIGg9bGVuZ3RoKHZhbGlkYXRpb24pKQpNQVBFKG5haXZlJG1lYW4sIHZhbGlkYXRpb24pICogMTAwCgpgYGAKClRoYXQgZ2l2ZXMgdXMgYW4gTUFQRSBvZiAyNy4wNCUuIFRoYXTigJlzIHRoZSBzY29yZSB0byBiZWF0LiBCeSB0aGUgd2F5LCByZW1vdmUgdGhlIHMgZnJvbSDigJxzbmFpdmXigJ0gYW5kIHlvdSBoYXZlIHRoZSBjb2RlIGZvciBzaW1wbGUgbmFpdmUuIEhlcmUgaXMgaG93IHRvIHBsb3QgdGhlIGZvcmVjYXN0OgoKYGBge3J9CgpwbG90KGRhdGEsIGNvbD0iYmx1ZSIsIHhsYWI9IlllYXIiLCB5bGFiPSJQYXNzZW5nZXJzIiwgbWFpbj0iU2Vhc29uYWwgTmFpdmUgRm9yZWNhc3QiLCB0eXBlPSdsJykKbGluZXMobmFpdmUkbWVhbiwgY29sPSJyZWQiLCBsd2Q9MikKICAgICAgCmBgYAoKIyAyLiBFeHBvbmVudGlhbCBTbW9vdGhpbmcKClRoZXJlIGFyZSBtYW55IHdheXMgdG8gZG8gZXhwb25lbnRpYWwgc21vb3RoaW5nLiBUaGUgaWRlYSBpcyBhbHdheXMgdG8gaGF2ZSBhIGRlY2xpbmluZyB3ZWlnaHQgZ2l2ZW4gdG8gb2JzZXJ2YXRpb25zLiBUaGUgbW9yZSByZWNlbnQgYW4gb2JzZXJ2YXRpb24sIHRoZSBtb3JlIGltcG9ydGFuY2UgaXQgd2lsbCBoYXZlIGluIG91ciBmb3JlY2FzdC4KUGFyYW1ldGVycyBjYW4gYWxzbyBiZSBhZGRlZC4gWW91IGNhbiBmb3IgaW5zdGFuY2UgYWRkIGEgdHJlbmQgcGFyYW1lbnRlciAoSG9sdCBtZXRob2QpIG9yIGFkZCBhIHNlYXNvbmFsaXR5IChIb2x0LVdpbnRlcnMpLgoKIyAyLjEgU3RhdGUgU3BhY2UgTW9kZWxzCgpXaXRoIHRoZSBGb3JlY2FzdCBQYWNrYWdlLCBzbW9vdGhpbmcgbWV0aG9kcyBjYW4gYmUgcGxhY2VkIHdpdGhpbiB0aGUgc3RydWN0dXJlIG9mIHN0YXRlIHNwYWNlIG1vZGVscy4gQnkgdXNpbmcgdGhpcyBzdHJ1Y3R1cmUsIHdlIGNhbiBmaW5kIHRoZSBvcHRpbWFsIGV4cG9uZW50aWFsIHNtb290aGluZyBtb2RlbCwgdXNpbmcgdGhlIGV0cyBmdW5jdGlvbi4KCmBgYHtyfQoKZXRzX21vZGVsID0gZXRzKHRyYWluaW5nLCBhbGxvdy5tdWx0aXBsaWNhdGl2ZS50cmVuZCA9IFRSVUUpCnN1bW1hcnkoZXRzX21vZGVsKQoKYGBgCgpXZSBzZWUgRVRTIChNLCBNZCwgTSkuIFRoaXMgbWVhbnMgd2UgaGF2ZSBhbiBldHMgbW9kZWwgd2l0aCBtdWx0aXBsaWNhdGl2ZSBlcnJvcnMsIGEgbXVsdGlwbGljYXRpdmUgdHJlbmQgYW5kIGEgbXVsdGlwbGljYXRpdmUgc2Vhc29uYWxpdHkuIEJhc2ljYWxseSwgbXVsdGlwbGljYXRpdmUgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGlzIOKAnGFtcGxpZmllZOKAnSBvdmVyIHRpbWUuIEZvciBpbnN0YW5jZSB0aGUgdHJlbmQgaXMgZ2V0dGluZyBiaWdnZXIgYW5kIGJpZ2dlciBhcyB0aW1lIGdvZXMgYnkgKG91ciBjYXNlIGhlcmUpLgpIZXJlIGlzIGhvdyB0byBmb3JlY2FzdCB1c2luZyB0aGUgZXN0aW1hdGVkIG9wdGltYWwgc21vb3RoaW5nIG1vZGVsOgoKYGBge3J9CgpldHNfZm9yZWNhc3QgPSBmb3JlY2FzdChldHNfbW9kZWwsIGg9bGVuZ3RoKHZhbGlkYXRpb24pKQpNQVBFKGV0c19mb3JlY2FzdCRtZWFuLCB2YWxpZGF0aW9uKSAqMTAwCgpgYGAKCldlIHNlZSB0aGF0IHRoZSB1cHdhcmQgdHJlbmQgaW4gZGVtYW5kIGlzIGJlaW5nIGNhcHR1cmUgYSBsaXR0bGUgYml0IChmYXIgZnJvbSBwZXJmZWN0LCBiZXR0ZXIgdGhhbiBuYWl2ZSkuIEl0IGdpdmVzIGFuIE1BUEUgb2YgMTIuNiUuCgojIDIuMiBEb3VibGUgU2Vhc29uYWwgSG9sdC1XaW50ZXJzCgpUaGUgZXRzIGZ1bmN0aW9uIGlzIGdvb2QsIGJ1dCBpdCBvbmx5IGFsbG93cyBmb3Igb25lIHNlYXNvbmFsaXR5LiBTb21ldGltZXMsIHRoZSBkYXRhIHdlIGhhdmUgY2FuIGJlIGNvbXBvc2VkIG9mIG11bHRpcGxlIHNlYXNvbmFsaXRpZXMgKG1vbnRobHkgYW5kIHllYXJseSBmb3IgaW5zdGFuY2UpLgpEb3VibGUgU2Vhc29uYWwgSG9sdC1XaW50ZXJzIChEU0hXKSBhbGxvd3MgZm9yIHR3byBzZWFzb25hbGl0aWVzOiBhIHNtYWxsZXIgb25lIHJlcGVhdGVkIG9mdGVuIGFuZCBhIGJpZ2dlciBvbmUgcmVwZWF0ZWQgbGVzcyBvZnRlbi4gRm9yIHRoZSBtZXRob2QgdG8gd29yayBob3dldmVyLCB0aGUgc2Vhc29uYWxpdGllcyBuZWVkIHRvIGJlIG5lc3RlZCwgbWVhbmluZyBvbmUgbXVzdCBiZSBhbiBpbnRlZ2VyIG11bHRpcGxlIG9mIHRoZSBvdGhlciAoMiBhbmQgNCwgMjQgYW5kIDE2OCwgZXRjLikuClRoZSBjb2RlIGhlcmUgaXMgYSBiaXQgZGlmZmVyZW50IHNpbmNlIHdlIG5lZWQgdG8gc3BlY2lmeSB0aGUgbGVuZ2h0cyBvZiBvdXIgdHdvIHNlYXNvbmFsaXRpZXMgKHdoaWNoIGlzIG5vdCBhbHdheXMgc29tZXRoaW5nIHdlIGtub3cpIGFuZCB0aGUgZm9yZWNhc3QgaXMgY29tcHV0ZWQgZGlyZWN0bHkgd2hlbiBjcmVhdGluZyB0aGUgbW9kZWwgd2l0aCB0aGUgZHNodyBmdW5jdGlvbi4KCmBgYHtyfQoKZHNod19tb2RlbCA9IGRzaHcodHJhaW5pbmcsIHBlcmlvZDE9NCwgcGVyaW9kMiA9IDEyLCBoPWxlbmd0aCh2YWxpZGF0aW9uKSkKTUFQRShkc2h3X21vZGVsJG1lYW4sIHZhbGlkYXRpb24pKjEwMAoKYGBgCldlIGdldCBhIE1BUEUgb2YgMy43JSB3aXRoIHRoaXMgbWV0aG9kIQoKIyAzLiBCQVRTIGFuZCBUQkFUUwpEU0hXIGlzIGdvb2QsIGJ1dCBzb21lIHByb2Nlc3NlcyBoYXZlIG1vcmUgY29tcGxleCBzZWFzb25hbGl0aWVzLCB3aGljaCBvdXIgcHJldmlvdXMgZnVuY3Rpb25zIGNhbm5vdCBoYW5kbGUuIEluZGVlZCwgeW91IGNvdWxkIGhhdmUgYm90aCBhIHdlZWtseSBhbmQgeWVhcmx5IHNlYXNvbmFsaXR5LiBZb3UgY291bGQgZXZlbiBoYXZlIG1vcmUgdGhhbiAyIQpCQVRTIGFuZCBUQkFUUyBhbGxvdyBmb3IgbXVsdGlwbGUgc2Vhc29uYWxpdGllcy4gVEJBVFMgaXMgYSBtb2RpZmljYXRpb24gKGFuIGltcHJvdmVtZW50IHJlYWxseSkgb2YgQkFUUyB0aGF0IGFsbG93cyBmb3IgbXVsdGlwbGUgbm9uLWludGVnZXIgc2Vhc29uYWxpdHkgY3ljbGVzLgpIZXJlIGlzIGhvdyB0byBidWlsZCBhIFRCQVRTIG1vZGVsIGFuZCBmb3JlY2FzdCB3aXRoIGl0OgoKYGBge3J9Cgp0YmF0c19tb2RlbCA9IHRiYXRzKHRyYWluaW5nKQp0YmF0c19mb3JlY2FzdCA9IGZvcmVjYXN0KHRiYXRzX21vZGVsLCBoPWxlbmd0aCh2YWxpZGF0aW9uKSkKTUFQRSh0YmF0c19mb3JlY2FzdCRtZWFuLCB2YWxpZGF0aW9uKSAqIDEwMAoKYGBgCldlIGdldCBhIE1BUEUgb2YgMTIuOSUgZm9yIHRoaXMgbWV0aG9kLgoKIyA0LiBBUklNQS9TQVJJTUEgbW9kZWxzCgpBUklNQSBtb2RlbHMgY29udGFpbiB0aHJlZSB0aGluZ3M6CkFSKHApOiBhdXRvcmVncmVzc2l2ZSBwYXJ0IG9mIHRoZSBtb2RlbC4gTWVhbnMgdGhhdCB3ZSB1c2UgcCBwYXN0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSB0aW1lc2VyaWVzIGFzIHByZWRpY3RvcnMuCkRpZmZlcmVuY2luZyAoZCk6IFVzZWQgdG8gdHJhbnNmb3JtIHRoZSB0aW1lc2VyaWVzIGludG8gYSBzdGF0aW9uYXJ5IG9uZSBieSB0YWtpbmcgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gc3VjY2Vzc2l2ZSBvYnNlcnZhdGlvbnMgYXQgYXBwcm9wcmlhdGUgbGFncyBkLgpNQShxKTogdXNlcyBxIHBhc3QgZm9yZWNhc3QgZXJyb3JzIGFzIHByZWRpY3RvcnMuClRoYXTigJlzIGl0IGZvciBBUklNQSBidXQgaWYgeW91IGtub3cgdGhlIGRhdGEgeW91IGhhdmUgaXMgc2Vhc29uYWwsIHRoZW4geW91IG5lZWQgbW9yZS4gVGhhdOKAmXMgd2hlcmUgU0FSSU1BIGNvbWVzIGludG8gcGxheS4KU0FSSU1BIGFkZHMgYSBzZWFzb25hbCBwYXJ0IHRvIHRoZSBtb2RlbC4KClRoZSBhdXRvLmFyaW1hIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIHJldHVybiB0aGUgYmVzdCBlc3RpbWF0ZWQgbW9kZWwuIEhlcmUgaXMgdGhlIGNvZGU6CgpgYGB7cn0KCmFyaW1hX29wdGltYWwgPSBhdXRvLmFyaW1hKHRyYWluaW5nKQoKYGBgCgpUaGUgZnVuY3Rpb24gcmV0dXJuZWQgdGhlIGZvbGxvd2luZyBtb2RlbDogQVJJTUEoMCwxLDEpKDEsMSwwKVsxMl0uClRvIGZvcmVjYXN0IGEgU0FSSU1BIG1vZGVsICh3aGljaCBpcyB3aGF0IHdlIGhhdmUgaGVyZSBzaW5jZSB3ZSBoYXZlIGEgc2Vhc29uYWwgcGFydCksIHdlIGNhbiB1c2UgdGhlIHNhcmltYS5mb3IgZnVuY3Rpb24gZnJvbSB0aGUgYXN0c2EgcGFja2FnZS4KCmBgYHtyfQoKbGlicmFyeShhc3RzYSkKCnNhcmltYV9mIDwtIHNhcmltYS5mb3IodHJhaW5pbmcsIG4uYWhlYWQ9bGVuZ3RoKHZhbGlkYXRpb24pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwPTAsZD0xLHE9MSxQPTEsRD0xLFE9MCxTPTEyKQoKTUFQRShzYXJpbWFfZiRwcmVkLCB2YWxpZGF0aW9uKSAqIDEwMAoKYGBgCgpXZSBnZXQgYSBNQVBFIG9mIDYuNSUgd2l0aCB0aGlzIFNBUklNQSBtb2RlbC4KCkp1c3Qgc28geW91IGtub3csIHdlIGNvdWxkIGluIHRoZW9yeSBjb21wbGV4aWZ5IHRoaW5ncyBldmVuIG1vcmUgYnkgYWRkaW5nIGV4b2dlbm91cyB2YXJpYWJsZXMgKGV4cGxhbmF0b3J5IHZhcmlhYmxlcykgdG8gYW4gQVJJTUEvU0FSSU1BIG1vZGVsLCB3aGljaCB3b3VsZCBtYWtlIGl0IFNBUklNQVguCkZvciB0aGlzIGRhdGEsIERTSFcgZ2F2ZSB0aGUgYmVzdCByZXN1bHRzLiBLZWVwIGluIG1pbmQgaG93ZXZlciB0aGF0IG5vIG1vZGVsIGRvZXMgYmVzdCBhbGwgdGhlIHRpbWUu