diff --git a/examples/README.md b/examples/README.md
index 39b28c5b..d5fc5527 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,8 +1,8 @@
# Forecasting examples
-This folder contains Python and R examples for building forecasting solutions presented in Python Jupyter notebooks and R Markdown files, respectively. The examples are organized according to forecasting scenarios in different use cases with each subdirectory under `examples/` named after the specific use case.
+This folder contains Python and R examples for building forecasting solutions presented in Python Jupyter notebooks and R Markdown files, respectively. The examples are organized according to forecasting scenarios in different use cases with each subdirectory under `examples/` named after the specific use case.
-At the moment, the repository contains a single retail sales forecasting scenario utilizing [Dominick's OrangeJuice data set](https://www.chicagobooth.edu/research/kilts/datasets/dominicks). The name of the directory is `grocery_sales`.
+At the moment, the repository contains a single retail sales forecasting scenario utilizing [Dominick's OrangeJuice data set](https://www.chicagobooth.edu/research/kilts/datasets/dominicks). The name of the directory is `grocery_sales`.
## Summary
diff --git a/examples/grocery_sales/R/01_dataprep.Rmd b/examples/grocery_sales/R/01_dataprep.Rmd
index 05cd7d02..d4f2881d 100644
--- a/examples/grocery_sales/R/01_dataprep.Rmd
+++ b/examples/grocery_sales/R/01_dataprep.Rmd
@@ -4,7 +4,7 @@ output: html_notebook
---
_Copyright (c) Microsoft Corporation._
-_Licensed under the MIT License._
+_Licensed under the MIT License._
In this notebook, we generate the datasets that will be used for model training and validating.
diff --git a/examples/grocery_sales/R/01_dataprep.nb.html b/examples/grocery_sales/R/01_dataprep.nb.html
index bf37aa54..3b86bdb0 100644
--- a/examples/grocery_sales/R/01_dataprep.nb.html
+++ b/examples/grocery_sales/R/01_dataprep.nb.html
@@ -4,194 +4,228 @@
-
+
-
-
-
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
-
In this notebook, we generate the datasets that will be used for model training and validating.
-
The orange juice dataset comes from the bayesm package, and gives pricing and sales figures over time for a variety of orange juice brands in several stores in Florida. Rather than installing the entire package (which is very complex), we download the dataset itself from the GitHub mirror of the CRAN repository.
-
-
-
-
# download the data from the GitHub mirror of the bayesm package source
+
+ Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
+ In this notebook, we generate the datasets that will be used for model training and validating.
+ The orange juice dataset comes from the bayesm package, and gives pricing and sales figures over time for a
+ variety of orange juice brands in several stores in Florida. Rather than installing the entire package (which is
+ very complex), we download the dataset itself from the GitHub mirror of the CRAN repository.
+
+
+
+ # download the data from the GitHub mirror of the bayesm package source
ojfile <- tempfile(fileext=".rda")
download.file("https://github.com/cran/bayesm/raw/master/data/orangeJuice.rda", ojfile)
load(ojfile)
file.remove(ojfile)
-
-
-
-The dataset generation parameters are obtained from the file ojdata_forecast_settings.yaml
; you can modify that file to vary the experimental setup. The settings are
-
-
-
-
-
-
-
-
-
-
-
-N_SPLITS |
-The number of splits to make. |
-10 |
-
-
-HORIZON |
-The forecast horizon for the test dataset for each split. |
-2 |
-
-
-GAP |
-The gap in weeks from the end of the training period to the start of the testing period; see below. |
-2 |
-
-
-FIRST_WEEK |
-The first week of data to use. |
-40 |
-
-
-LAST_WEEK |
-The last week of data to use. |
-156 |
-
-
-START_DATE |
-The actual calendar date for the start of the first week in the data. |
-1989-09-14 |
-
-
-
-A complicating factor is that the data does not include every possible combination of store, brand and date, so we have to pad out the missing rows with complete
. In addition, one store/brand combination has no data beyond week 156; we therefore end the analysis at this week. We also do not fill in the missing values in the data, as many of the modelling functions in the fable package can handle this innately.
-
-
-
-library(tidyr)
+
+
+
+ The dataset generation parameters are obtained from the file ojdata_forecast_settings.yaml
; you can
+ modify that file to vary the experimental setup. The settings are
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ N_SPLITS |
+ The number of splits to make. |
+ 10 |
+
+
+ HORIZON |
+ The forecast horizon for the test dataset for each split. |
+ 2 |
+
+
+ GAP |
+ The gap in weeks from the end of the training period to the start of the testing period; see below. |
+ 2 |
+
+
+ FIRST_WEEK |
+ The first week of data to use. |
+ 40 |
+
+
+ LAST_WEEK |
+ The last week of data to use. |
+ 156 |
+
+
+ START_DATE |
+ The actual calendar date for the start of the first week in the data. |
+ 1989-09-14 |
+
+
+
+ A complicating factor is that the data does not include every possible combination of store, brand and date, so
+ we have to pad out the missing rows with complete
. In addition, one store/brand combination has no
+ data beyond week 156; we therefore end the analysis at this week. We also do not fill in the missing
+ values in the data, as many of the modelling functions in the fable package can handle this innately.
+
+
+
+ library(tidyr)
library(dplyr)
library(tsibble)
library(feasts)
@@ -307,33 +351,37 @@ oj_data <- orangeJuice$yx %>%
complete(store, brand, week) %>%
mutate(week=yearweek(start_date + week*7)) %>%
as_tsibble(index=week, key=c(store, brand))
-
-
-
-Here are some glimpses of what the data looks like. The dependent variable is logmove
, the logarithm of the total sales for a given brand and store, in a particular week.
-
-
-
-head(oj_data)
-
-
-
-
-
-
-The time series plots for a small subset of brands and stores are shown below. We can make the following observations:
-
-- There appears to be little seasonal variation in sales (probably because Florida is a state without very different seasons). In any case, with less than 2 years of observations, the time series is not long enough for many model-fitting functions in the fable package to automatically estimate seasonal parameters.
-- While some store/brand combinations show weak trends over time, this is far from universal.
-- Different brands can exhibit very different behaviour, especially in terms of variation about the mean.
-- Many of the time series have missing values, indicating that the dataset is incomplete.
-
-
-
-
-library(ggplot2)
+
+
+
+
The time series plots for a small subset of brands and stores are shown below. We can make the following
+ observations:
+
+ - There appears to be little seasonal variation in sales (probably because Florida is a state without very
+ different seasons). In any case, with less than 2 years of observations, the time series is not long enough for
+ many model-fitting functions in the fable package to automatically estimate seasonal parameters.
+ - While some store/brand combinations show weak trends over time, this is far from universal.
+ - Different brands can exhibit very different behaviour, especially in terms of variation about the mean.
+ - Many of the time series have missing values, indicating that the dataset is incomplete.
+
+
+
+
+
library(ggplot2)
oj_data %>%
filter(store < 25, brand < 5) %>%
@@ -341,17 +389,30 @@ oj_data %>%
geom_line() +
scale_x_date(labels=NULL) +
facet_grid(vars(store), vars(brand), labeller="label_both")
-
-
-
-
-
-
-
Finally, we split the dataset into separate samples for training and testing. The schema used is broadly time series cross-validation, whereby we train a model on data up to time \(t\), test it on data for times \(t+1\) to \(t+k\), then train on data up to time \(t+k\), test it on data for times \(t+k+1\) to \(t+2k\), and so on. In this specific case study, however, we introduce a small extra piece of complexity based on discussions with domain experts. We train a model on data up to week \(t\), then test it on week \(t+2\) to \(t+3\). Then we train on data up to week \(t+2\), and test it on weeks \(t+4\) to \(t+5\), and so on. There is thus always a gap of one week between the training and test samples. The reason for this is because in reality, inventory planning always takes some time; the gap allows store managers to prepare the stock based on the forecasted demand.
-
-
-
-
subset_oj_data <- function(start, end)
+
+
+
+
+
+
+ Finally, we split the dataset into separate samples for training and testing. The schema used is broadly time
+ series cross-validation, whereby we train a model on data up to time \(t\), test
+ it on data for times \(t+1\) to \(t+k\), then
+ train on data up to time \(t+k\), test it on data for times \(t+k+1\) to \(t+2k\), and so on. In this specific
+ case study, however, we introduce a small extra piece of complexity based on discussions with domain experts. We
+ train a model on data up to week \(t\), then test it on week \(t+2\) to \(t+3\). Then we train on data up to week
+ \(t+2\), and test it on weeks \(t+4\) to \(t+5\), and so on. There is thus always a gap of one week between the training and
+ test samples. The reason for this is because in reality, inventory planning always takes some time; the gap allows
+ store managers to prepare the stock based on the forecasted demand.
+
+
+
+ subset_oj_data <- function(start, end)
{
start <- yearweek(start_date + start*7)
end <- yearweek(start_date + end*7)
@@ -364,84 +425,87 @@ oj_test <- lapply(train_periods, function(i) subset_oj_data(i + settings$GAP,
save(oj_train, oj_test, file=here::here("examples/grocery_sales/R/data.Rdata"))
head(oj_train[[1]])
-
-
-
-
-
-head(oj_test[[1]])
-
-
-
-
-
+
+
-LS0tCnRpdGxlOiBEYXRhIHByZXBhcmF0aW9uCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCl9Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5fPGJyLz4KX0xpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS5fCgpJbiB0aGlzIG5vdGVib29rLCB3ZSBnZW5lcmF0ZSB0aGUgZGF0YXNldHMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIG1vZGVsIHRyYWluaW5nIGFuZCB2YWxpZGF0aW5nLiAKClRoZSBvcmFuZ2UganVpY2UgZGF0YXNldCBjb21lcyBmcm9tIHRoZSBiYXllc20gcGFja2FnZSwgYW5kIGdpdmVzIHByaWNpbmcgYW5kIHNhbGVzIGZpZ3VyZXMgb3ZlciB0aW1lIGZvciBhIHZhcmlldHkgb2Ygb3JhbmdlIGp1aWNlIGJyYW5kcyBpbiBzZXZlcmFsIHN0b3JlcyBpbiBGbG9yaWRhLiBSYXRoZXIgdGhhbiBpbnN0YWxsaW5nIHRoZSBlbnRpcmUgcGFja2FnZSAod2hpY2ggaXMgdmVyeSBjb21wbGV4KSwgd2UgZG93bmxvYWQgdGhlIGRhdGFzZXQgaXRzZWxmIGZyb20gdGhlIEdpdEh1YiBtaXJyb3Igb2YgdGhlIENSQU4gcmVwb3NpdG9yeS4KCmBgYHtyLCByZXN1bHRzPSJoaWRlIiwgbWVzc2FnZT1GQUxTRX0KIyBkb3dubG9hZCB0aGUgZGF0YSBmcm9tIHRoZSBHaXRIdWIgbWlycm9yIG9mIHRoZSBiYXllc20gcGFja2FnZSBzb3VyY2UKb2pmaWxlIDwtIHRlbXBmaWxlKGZpbGVleHQ9Ii5yZGEiKQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20vY3Jhbi9iYXllc20vcmF3L21hc3Rlci9kYXRhL29yYW5nZUp1aWNlLnJkYSIsIG9qZmlsZSkKbG9hZChvamZpbGUpCmZpbGUucmVtb3ZlKG9qZmlsZSkKYGBgCgpUaGUgZGF0YXNldCBnZW5lcmF0aW9uIHBhcmFtZXRlcnMgYXJlIG9idGFpbmVkIGZyb20gdGhlIGZpbGUgYG9qZGF0YV9mb3JlY2FzdF9zZXR0aW5ncy55YW1sYDsgeW91IGNhbiBtb2RpZnkgdGhhdCBmaWxlIHRvIHZhcnkgdGhlIGV4cGVyaW1lbnRhbCBzZXR1cC4gVGhlIHNldHRpbmdzIGFyZQoKfCBQYXJhbWV0ZXIgfCBEZXNjcmlwdGlvbiB8IERlZmF1bHQgfCAKfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tfAp8IGBOX1NQTElUU2AgfCBUaGUgbnVtYmVyIG9mIHNwbGl0cyB0byBtYWtlLiB8IDEwIHwKfCBgSE9SSVpPTmAgfCBUaGUgZm9yZWNhc3QgaG9yaXpvbiBmb3IgdGhlIHRlc3QgZGF0YXNldCBmb3IgZWFjaCBzcGxpdC4gfCAyIHwKfCBgR0FQYCB8IFRoZSBnYXAgaW4gd2Vla3MgZnJvbSB0aGUgZW5kIG9mIHRoZSB0cmFpbmluZyBwZXJpb2QgdG8gdGhlIHN0YXJ0IG9mIHRoZSB0ZXN0aW5nIHBlcmlvZDsgc2VlIGJlbG93LiB8IDIgfAp8IGBGSVJTVF9XRUVLYCB8IFRoZSBmaXJzdCB3ZWVrIG9mIGRhdGEgdG8gdXNlLiB8IDQwIHwKfCBgTEFTVF9XRUVLYCB8IFRoZSBsYXN0IHdlZWsgb2YgZGF0YSB0byB1c2UuIHwgMTU2IHwKfCBgU1RBUlRfREFURWAgfCBUaGUgYWN0dWFsIGNhbGVuZGFyIGRhdGUgZm9yIHRoZSBzdGFydCBvZiB0aGUgZmlyc3Qgd2VlayBpbiB0aGUgZGF0YS4gfCBgMTk4OS0wOS0xNGAgfAoKQSBjb21wbGljYXRpbmcgZmFjdG9yIGlzIHRoYXQgdGhlIGRhdGEgZG9lcyBub3QgaW5jbHVkZSBldmVyeSBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiBzdG9yZSwgYnJhbmQgYW5kIGRhdGUsIHNvIHdlIGhhdmUgdG8gcGFkIG91dCB0aGUgbWlzc2luZyByb3dzIHdpdGggYGNvbXBsZXRlYC4gSW4gYWRkaXRpb24sIG9uZSBzdG9yZS9icmFuZCBjb21iaW5hdGlvbiBoYXMgbm8gZGF0YSBiZXlvbmQgd2VlayAxNTY7IHdlIHRoZXJlZm9yZSBlbmQgdGhlIGFuYWx5c2lzIGF0IHRoaXMgd2Vlay4gV2UgYWxzbyBkbyBfbm90XyBmaWxsIGluIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YSwgYXMgbWFueSBvZiB0aGUgbW9kZWxsaW5nIGZ1bmN0aW9ucyBpbiB0aGUgZmFibGUgcGFja2FnZSBjYW4gaGFuZGxlIHRoaXMgaW5uYXRlbHkuCgpgYGB7ciwgcmVzdWx0cz0iaGlkZSIsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodHNpYmJsZSkKbGlicmFyeShmZWFzdHMpCmxpYnJhcnkoZmFibGUpCgpzZXR0aW5ncyA8LSB5YW1sOjpyZWFkX3lhbWwoaGVyZTo6aGVyZSgiZXhhbXBsZXMvZ3JvY2VyeV9zYWxlcy9SL2ZvcmVjYXN0X3NldHRpbmdzLnlhbWwiKSkKc3RhcnRfZGF0ZSA8LSBhcy5EYXRlKHNldHRpbmdzJFNUQVJUX0RBVEUpCnRyYWluX3BlcmlvZHMgPC0gc2VxKHRvPXNldHRpbmdzJExBU1RfV0VFSyAtIHNldHRpbmdzJEhPUklaT04gLSBzZXR0aW5ncyRHQVAgKyAxLAogICAgICAgICAgICAgICAgICAgICBieT1zZXR0aW5ncyRIT1JJWk9OLAogICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PXNldHRpbmdzJE5fU1BMSVRTKQoKb2pfZGF0YSA8LSBvcmFuZ2VKdWljZSR5eCAlPiUKICAgIGNvbXBsZXRlKHN0b3JlLCBicmFuZCwgd2VlaykgJT4lCiAgICBtdXRhdGUod2Vlaz15ZWFyd2VlayhzdGFydF9kYXRlICsgd2Vlayo3KSkgJT4lCiAgICBhc190c2liYmxlKGluZGV4PXdlZWssIGtleT1jKHN0b3JlLCBicmFuZCkpCmBgYAoKSGVyZSBhcmUgc29tZSBnbGltcHNlcyBvZiB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UuIFRoZSBkZXBlbmRlbnQgdmFyaWFibGUgaXMgYGxvZ21vdmVgLCB0aGUgbG9nYXJpdGhtIG9mIHRoZSB0b3RhbCBzYWxlcyBmb3IgYSBnaXZlbiBicmFuZCBhbmQgc3RvcmUsIGluIGEgcGFydGljdWxhciB3ZWVrLgoKYGBge3J9CmhlYWQob2pfZGF0YSkKYGBgCgpUaGUgdGltZSBzZXJpZXMgcGxvdHMgZm9yIGEgc21hbGwgc3Vic2V0IG9mIGJyYW5kcyBhbmQgc3RvcmVzIGFyZSBzaG93biBiZWxvdy4gV2UgY2FuIG1ha2UgdGhlIGZvbGxvd2luZyBvYnNlcnZhdGlvbnM6CgotIFRoZXJlIGFwcGVhcnMgdG8gYmUgbGl0dGxlIHNlYXNvbmFsIHZhcmlhdGlvbiBpbiBzYWxlcyAocHJvYmFibHkgYmVjYXVzZSBGbG9yaWRhIGlzIGEgc3RhdGUgd2l0aG91dCB2ZXJ5IGRpZmZlcmVudCBzZWFzb25zKS4gSW4gYW55IGNhc2UsIHdpdGggbGVzcyB0aGFuIDIgeWVhcnMgb2Ygb2JzZXJ2YXRpb25zLCB0aGUgdGltZSBzZXJpZXMgaXMgbm90IGxvbmcgZW5vdWdoIGZvciBtYW55IG1vZGVsLWZpdHRpbmcgZnVuY3Rpb25zIGluIHRoZSBmYWJsZSBwYWNrYWdlIHRvIGF1dG9tYXRpY2FsbHkgZXN0aW1hdGUgc2Vhc29uYWwgcGFyYW1ldGVycy4KLSBXaGlsZSBzb21lIHN0b3JlL2JyYW5kIGNvbWJpbmF0aW9ucyBzaG93IHdlYWsgdHJlbmRzIG92ZXIgdGltZSwgdGhpcyBpcyBmYXIgZnJvbSB1bml2ZXJzYWwuCi0gRGlmZmVyZW50IGJyYW5kcyBjYW4gZXhoaWJpdCB2ZXJ5IGRpZmZlcmVudCBiZWhhdmlvdXIsIGVzcGVjaWFsbHkgaW4gdGVybXMgb2YgdmFyaWF0aW9uIGFib3V0IHRoZSBtZWFuLgotIE1hbnkgb2YgdGhlIHRpbWUgc2VyaWVzIGhhdmUgbWlzc2luZyB2YWx1ZXMsIGluZGljYXRpbmcgdGhhdCB0aGUgZGF0YXNldCBpcyBpbmNvbXBsZXRlLgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpsaWJyYXJ5KGdncGxvdDIpCgpval9kYXRhICU+JQogICAgZmlsdGVyKHN0b3JlIDwgMjUsIGJyYW5kIDwgNSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHg9d2VlaywgeT1sb2dtb3ZlKSkgKwogICAgICAgIGdlb21fbGluZSgpICsKICAgICAgICBzY2FsZV94X2RhdGUobGFiZWxzPU5VTEwpICsKICAgICAgICBmYWNldF9ncmlkKHZhcnMoc3RvcmUpLCB2YXJzKGJyYW5kKSwgbGFiZWxsZXI9ImxhYmVsX2JvdGgiKQpgYGAKCkZpbmFsbHksIHdlIHNwbGl0IHRoZSBkYXRhc2V0IGludG8gc2VwYXJhdGUgc2FtcGxlcyBmb3IgdHJhaW5pbmcgYW5kIHRlc3RpbmcuIFRoZSBzY2hlbWEgdXNlZCBpcyBicm9hZGx5IHRpbWUgc2VyaWVzIGNyb3NzLXZhbGlkYXRpb24sIHdoZXJlYnkgd2UgdHJhaW4gYSBtb2RlbCBvbiBkYXRhIHVwIHRvIHRpbWUgJHQkLCB0ZXN0IGl0IG9uIGRhdGEgZm9yIHRpbWVzICR0KzEkIHRvICR0K2skLCB0aGVuIHRyYWluIG9uIGRhdGEgdXAgdG8gdGltZSAkdCtrJCwgdGVzdCBpdCBvbiBkYXRhIGZvciB0aW1lcyAkdCtrKzEkIHRvICR0KzJrJCwgYW5kIHNvIG9uLiBJbiB0aGlzIHNwZWNpZmljIGNhc2Ugc3R1ZHksIGhvd2V2ZXIsIHdlIGludHJvZHVjZSBhIHNtYWxsIGV4dHJhIHBpZWNlIG9mIGNvbXBsZXhpdHkgYmFzZWQgb24gZGlzY3Vzc2lvbnMgd2l0aCBkb21haW4gZXhwZXJ0cy4gV2UgdHJhaW4gYSBtb2RlbCBvbiBkYXRhIHVwIHRvIHdlZWsgJHQkLCB0aGVuIHRlc3QgaXQgb24gd2VlayAkdCsyJCB0byAkdCszJC4gVGhlbiB3ZSB0cmFpbiBvbiBkYXRhIHVwIHRvIHdlZWsgJHQrMiQsIGFuZCB0ZXN0IGl0IG9uIHdlZWtzICR0KzQkIHRvICR0KzUkLCBhbmQgc28gb24uIFRoZXJlIGlzIHRodXMgYWx3YXlzIGEgZ2FwIG9mIG9uZSB3ZWVrIGJldHdlZW4gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNhbXBsZXMuIFRoZSByZWFzb24gZm9yIHRoaXMgaXMgYmVjYXVzZSBpbiByZWFsaXR5LCBpbnZlbnRvcnkgcGxhbm5pbmcgYWx3YXlzIHRha2VzIHNvbWUgdGltZTsgdGhlIGdhcCBhbGxvd3Mgc3RvcmUgbWFuYWdlcnMgdG8gcHJlcGFyZSB0aGUgc3RvY2sgYmFzZWQgb24gdGhlIGZvcmVjYXN0ZWQgZGVtYW5kLgoKYGBge3J9CnN1YnNldF9val9kYXRhIDwtIGZ1bmN0aW9uKHN0YXJ0LCBlbmQpCnsKICAgIHN0YXJ0IDwtIHllYXJ3ZWVrKHN0YXJ0X2RhdGUgKyBzdGFydCo3KQogICAgZW5kIDwtIHllYXJ3ZWVrKHN0YXJ0X2RhdGUgKyBlbmQqNykKICAgIGZpbHRlcihval9kYXRhLCB3ZWVrID49IHN0YXJ0LCB3ZWVrIDw9IGVuZCkKfQoKb2pfdHJhaW4gPC0gbGFwcGx5KHRyYWluX3BlcmlvZHMsIGZ1bmN0aW9uKGkpIHN1YnNldF9val9kYXRhKHNldHRpbmdzJEZJUlNUX1dFRUssIGkpKQpval90ZXN0IDwtIGxhcHBseSh0cmFpbl9wZXJpb2RzLCBmdW5jdGlvbihpKSBzdWJzZXRfb2pfZGF0YShpICsgc2V0dGluZ3MkR0FQLCBpICsgc2V0dGluZ3MkR0FQICsgc2V0dGluZ3MkSE9SSVpPTiAtIDEpKQoKc2F2ZShval90cmFpbiwgb2pfdGVzdCwgZmlsZT1oZXJlOjpoZXJlKCJleGFtcGxlcy9ncm9jZXJ5X3NhbGVzL1IvZGF0YS5SZGF0YSIpKQoKaGVhZChval90cmFpbltbMV1dKQoKaGVhZChval90ZXN0W1sxXV0pCmBgYAo=
+
+ LS0tCnRpdGxlOiBEYXRhIHByZXBhcmF0aW9uCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCl9Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5fPGJyLz4KX0xpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS5fCgpJbiB0aGlzIG5vdGVib29rLCB3ZSBnZW5lcmF0ZSB0aGUgZGF0YXNldHMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIG1vZGVsIHRyYWluaW5nIGFuZCB2YWxpZGF0aW5nLiAKClRoZSBvcmFuZ2UganVpY2UgZGF0YXNldCBjb21lcyBmcm9tIHRoZSBiYXllc20gcGFja2FnZSwgYW5kIGdpdmVzIHByaWNpbmcgYW5kIHNhbGVzIGZpZ3VyZXMgb3ZlciB0aW1lIGZvciBhIHZhcmlldHkgb2Ygb3JhbmdlIGp1aWNlIGJyYW5kcyBpbiBzZXZlcmFsIHN0b3JlcyBpbiBGbG9yaWRhLiBSYXRoZXIgdGhhbiBpbnN0YWxsaW5nIHRoZSBlbnRpcmUgcGFja2FnZSAod2hpY2ggaXMgdmVyeSBjb21wbGV4KSwgd2UgZG93bmxvYWQgdGhlIGRhdGFzZXQgaXRzZWxmIGZyb20gdGhlIEdpdEh1YiBtaXJyb3Igb2YgdGhlIENSQU4gcmVwb3NpdG9yeS4KCmBgYHtyLCByZXN1bHRzPSJoaWRlIiwgbWVzc2FnZT1GQUxTRX0KIyBkb3dubG9hZCB0aGUgZGF0YSBmcm9tIHRoZSBHaXRIdWIgbWlycm9yIG9mIHRoZSBiYXllc20gcGFja2FnZSBzb3VyY2UKb2pmaWxlIDwtIHRlbXBmaWxlKGZpbGVleHQ9Ii5yZGEiKQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20vY3Jhbi9iYXllc20vcmF3L21hc3Rlci9kYXRhL29yYW5nZUp1aWNlLnJkYSIsIG9qZmlsZSkKbG9hZChvamZpbGUpCmZpbGUucmVtb3ZlKG9qZmlsZSkKYGBgCgpUaGUgZGF0YXNldCBnZW5lcmF0aW9uIHBhcmFtZXRlcnMgYXJlIG9idGFpbmVkIGZyb20gdGhlIGZpbGUgYG9qZGF0YV9mb3JlY2FzdF9zZXR0aW5ncy55YW1sYDsgeW91IGNhbiBtb2RpZnkgdGhhdCBmaWxlIHRvIHZhcnkgdGhlIGV4cGVyaW1lbnRhbCBzZXR1cC4gVGhlIHNldHRpbmdzIGFyZQoKfCBQYXJhbWV0ZXIgfCBEZXNjcmlwdGlvbiB8IERlZmF1bHQgfCAKfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tfAp8IGBOX1NQTElUU2AgfCBUaGUgbnVtYmVyIG9mIHNwbGl0cyB0byBtYWtlLiB8IDEwIHwKfCBgSE9SSVpPTmAgfCBUaGUgZm9yZWNhc3QgaG9yaXpvbiBmb3IgdGhlIHRlc3QgZGF0YXNldCBmb3IgZWFjaCBzcGxpdC4gfCAyIHwKfCBgR0FQYCB8IFRoZSBnYXAgaW4gd2Vla3MgZnJvbSB0aGUgZW5kIG9mIHRoZSB0cmFpbmluZyBwZXJpb2QgdG8gdGhlIHN0YXJ0IG9mIHRoZSB0ZXN0aW5nIHBlcmlvZDsgc2VlIGJlbG93LiB8IDIgfAp8IGBGSVJTVF9XRUVLYCB8IFRoZSBmaXJzdCB3ZWVrIG9mIGRhdGEgdG8gdXNlLiB8IDQwIHwKfCBgTEFTVF9XRUVLYCB8IFRoZSBsYXN0IHdlZWsgb2YgZGF0YSB0byB1c2UuIHwgMTU2IHwKfCBgU1RBUlRfREFURWAgfCBUaGUgYWN0dWFsIGNhbGVuZGFyIGRhdGUgZm9yIHRoZSBzdGFydCBvZiB0aGUgZmlyc3Qgd2VlayBpbiB0aGUgZGF0YS4gfCBgMTk4OS0wOS0xNGAgfAoKQSBjb21wbGljYXRpbmcgZmFjdG9yIGlzIHRoYXQgdGhlIGRhdGEgZG9lcyBub3QgaW5jbHVkZSBldmVyeSBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiBzdG9yZSwgYnJhbmQgYW5kIGRhdGUsIHNvIHdlIGhhdmUgdG8gcGFkIG91dCB0aGUgbWlzc2luZyByb3dzIHdpdGggYGNvbXBsZXRlYC4gSW4gYWRkaXRpb24sIG9uZSBzdG9yZS9icmFuZCBjb21iaW5hdGlvbiBoYXMgbm8gZGF0YSBiZXlvbmQgd2VlayAxNTY7IHdlIHRoZXJlZm9yZSBlbmQgdGhlIGFuYWx5c2lzIGF0IHRoaXMgd2Vlay4gV2UgYWxzbyBkbyBfbm90XyBmaWxsIGluIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YSwgYXMgbWFueSBvZiB0aGUgbW9kZWxsaW5nIGZ1bmN0aW9ucyBpbiB0aGUgZmFibGUgcGFja2FnZSBjYW4gaGFuZGxlIHRoaXMgaW5uYXRlbHkuCgpgYGB7ciwgcmVzdWx0cz0iaGlkZSIsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodHNpYmJsZSkKbGlicmFyeShmZWFzdHMpCmxpYnJhcnkoZmFibGUpCgpzZXR0aW5ncyA8LSB5YW1sOjpyZWFkX3lhbWwoaGVyZTo6aGVyZSgiZXhhbXBsZXMvZ3JvY2VyeV9zYWxlcy9SL2ZvcmVjYXN0X3NldHRpbmdzLnlhbWwiKSkKc3RhcnRfZGF0ZSA8LSBhcy5EYXRlKHNldHRpbmdzJFNUQVJUX0RBVEUpCnRyYWluX3BlcmlvZHMgPC0gc2VxKHRvPXNldHRpbmdzJExBU1RfV0VFSyAtIHNldHRpbmdzJEhPUklaT04gLSBzZXR0aW5ncyRHQVAgKyAxLAogICAgICAgICAgICAgICAgICAgICBieT1zZXR0aW5ncyRIT1JJWk9OLAogICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PXNldHRpbmdzJE5fU1BMSVRTKQoKb2pfZGF0YSA8LSBvcmFuZ2VKdWljZSR5eCAlPiUKICAgIGNvbXBsZXRlKHN0b3JlLCBicmFuZCwgd2VlaykgJT4lCiAgICBtdXRhdGUod2Vlaz15ZWFyd2VlayhzdGFydF9kYXRlICsgd2Vlayo3KSkgJT4lCiAgICBhc190c2liYmxlKGluZGV4PXdlZWssIGtleT1jKHN0b3JlLCBicmFuZCkpCmBgYAoKSGVyZSBhcmUgc29tZSBnbGltcHNlcyBvZiB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UuIFRoZSBkZXBlbmRlbnQgdmFyaWFibGUgaXMgYGxvZ21vdmVgLCB0aGUgbG9nYXJpdGhtIG9mIHRoZSB0b3RhbCBzYWxlcyBmb3IgYSBnaXZlbiBicmFuZCBhbmQgc3RvcmUsIGluIGEgcGFydGljdWxhciB3ZWVrLgoKYGBge3J9CmhlYWQob2pfZGF0YSkKYGBgCgpUaGUgdGltZSBzZXJpZXMgcGxvdHMgZm9yIGEgc21hbGwgc3Vic2V0IG9mIGJyYW5kcyBhbmQgc3RvcmVzIGFyZSBzaG93biBiZWxvdy4gV2UgY2FuIG1ha2UgdGhlIGZvbGxvd2luZyBvYnNlcnZhdGlvbnM6CgotIFRoZXJlIGFwcGVhcnMgdG8gYmUgbGl0dGxlIHNlYXNvbmFsIHZhcmlhdGlvbiBpbiBzYWxlcyAocHJvYmFibHkgYmVjYXVzZSBGbG9yaWRhIGlzIGEgc3RhdGUgd2l0aG91dCB2ZXJ5IGRpZmZlcmVudCBzZWFzb25zKS4gSW4gYW55IGNhc2UsIHdpdGggbGVzcyB0aGFuIDIgeWVhcnMgb2Ygb2JzZXJ2YXRpb25zLCB0aGUgdGltZSBzZXJpZXMgaXMgbm90IGxvbmcgZW5vdWdoIGZvciBtYW55IG1vZGVsLWZpdHRpbmcgZnVuY3Rpb25zIGluIHRoZSBmYWJsZSBwYWNrYWdlIHRvIGF1dG9tYXRpY2FsbHkgZXN0aW1hdGUgc2Vhc29uYWwgcGFyYW1ldGVycy4KLSBXaGlsZSBzb21lIHN0b3JlL2JyYW5kIGNvbWJpbmF0aW9ucyBzaG93IHdlYWsgdHJlbmRzIG92ZXIgdGltZSwgdGhpcyBpcyBmYXIgZnJvbSB1bml2ZXJzYWwuCi0gRGlmZmVyZW50IGJyYW5kcyBjYW4gZXhoaWJpdCB2ZXJ5IGRpZmZlcmVudCBiZWhhdmlvdXIsIGVzcGVjaWFsbHkgaW4gdGVybXMgb2YgdmFyaWF0aW9uIGFib3V0IHRoZSBtZWFuLgotIE1hbnkgb2YgdGhlIHRpbWUgc2VyaWVzIGhhdmUgbWlzc2luZyB2YWx1ZXMsIGluZGljYXRpbmcgdGhhdCB0aGUgZGF0YXNldCBpcyBpbmNvbXBsZXRlLgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpsaWJyYXJ5KGdncGxvdDIpCgpval9kYXRhICU+JQogICAgZmlsdGVyKHN0b3JlIDwgMjUsIGJyYW5kIDwgNSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHg9d2VlaywgeT1sb2dtb3ZlKSkgKwogICAgICAgIGdlb21fbGluZSgpICsKICAgICAgICBzY2FsZV94X2RhdGUobGFiZWxzPU5VTEwpICsKICAgICAgICBmYWNldF9ncmlkKHZhcnMoc3RvcmUpLCB2YXJzKGJyYW5kKSwgbGFiZWxsZXI9ImxhYmVsX2JvdGgiKQpgYGAKCkZpbmFsbHksIHdlIHNwbGl0IHRoZSBkYXRhc2V0IGludG8gc2VwYXJhdGUgc2FtcGxlcyBmb3IgdHJhaW5pbmcgYW5kIHRlc3RpbmcuIFRoZSBzY2hlbWEgdXNlZCBpcyBicm9hZGx5IHRpbWUgc2VyaWVzIGNyb3NzLXZhbGlkYXRpb24sIHdoZXJlYnkgd2UgdHJhaW4gYSBtb2RlbCBvbiBkYXRhIHVwIHRvIHRpbWUgJHQkLCB0ZXN0IGl0IG9uIGRhdGEgZm9yIHRpbWVzICR0KzEkIHRvICR0K2skLCB0aGVuIHRyYWluIG9uIGRhdGEgdXAgdG8gdGltZSAkdCtrJCwgdGVzdCBpdCBvbiBkYXRhIGZvciB0aW1lcyAkdCtrKzEkIHRvICR0KzJrJCwgYW5kIHNvIG9uLiBJbiB0aGlzIHNwZWNpZmljIGNhc2Ugc3R1ZHksIGhvd2V2ZXIsIHdlIGludHJvZHVjZSBhIHNtYWxsIGV4dHJhIHBpZWNlIG9mIGNvbXBsZXhpdHkgYmFzZWQgb24gZGlzY3Vzc2lvbnMgd2l0aCBkb21haW4gZXhwZXJ0cy4gV2UgdHJhaW4gYSBtb2RlbCBvbiBkYXRhIHVwIHRvIHdlZWsgJHQkLCB0aGVuIHRlc3QgaXQgb24gd2VlayAkdCsyJCB0byAkdCszJC4gVGhlbiB3ZSB0cmFpbiBvbiBkYXRhIHVwIHRvIHdlZWsgJHQrMiQsIGFuZCB0ZXN0IGl0IG9uIHdlZWtzICR0KzQkIHRvICR0KzUkLCBhbmQgc28gb24uIFRoZXJlIGlzIHRodXMgYWx3YXlzIGEgZ2FwIG9mIG9uZSB3ZWVrIGJldHdlZW4gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNhbXBsZXMuIFRoZSByZWFzb24gZm9yIHRoaXMgaXMgYmVjYXVzZSBpbiByZWFsaXR5LCBpbnZlbnRvcnkgcGxhbm5pbmcgYWx3YXlzIHRha2VzIHNvbWUgdGltZTsgdGhlIGdhcCBhbGxvd3Mgc3RvcmUgbWFuYWdlcnMgdG8gcHJlcGFyZSB0aGUgc3RvY2sgYmFzZWQgb24gdGhlIGZvcmVjYXN0ZWQgZGVtYW5kLgoKYGBge3J9CnN1YnNldF9val9kYXRhIDwtIGZ1bmN0aW9uKHN0YXJ0LCBlbmQpCnsKICAgIHN0YXJ0IDwtIHllYXJ3ZWVrKHN0YXJ0X2RhdGUgKyBzdGFydCo3KQogICAgZW5kIDwtIHllYXJ3ZWVrKHN0YXJ0X2RhdGUgKyBlbmQqNykKICAgIGZpbHRlcihval9kYXRhLCB3ZWVrID49IHN0YXJ0LCB3ZWVrIDw9IGVuZCkKfQoKb2pfdHJhaW4gPC0gbGFwcGx5KHRyYWluX3BlcmlvZHMsIGZ1bmN0aW9uKGkpIHN1YnNldF9val9kYXRhKHNldHRpbmdzJEZJUlNUX1dFRUssIGkpKQpval90ZXN0IDwtIGxhcHBseSh0cmFpbl9wZXJpb2RzLCBmdW5jdGlvbihpKSBzdWJzZXRfb2pfZGF0YShpICsgc2V0dGluZ3MkR0FQLCBpICsgc2V0dGluZ3MkR0FQICsgc2V0dGluZ3MkSE9SSVpPTiAtIDEpKQoKc2F2ZShval90cmFpbiwgb2pfdGVzdCwgZmlsZT1oZXJlOjpoZXJlKCJleGFtcGxlcy9ncm9jZXJ5X3NhbGVzL1IvZGF0YS5SZGF0YSIpKQoKaGVhZChval90cmFpbltbMV1dKQoKaGVhZChval90ZXN0W1sxXV0pCmBgYAo=
+
-
+
-
+
-
+
-
+ $(document).ready(function () {
+ $('.tabset-dropdown > .nav-tabs > li').click(function () {
+ $(this).parent().toggleClass('nav-tabs-open')
+ });
+ });
+
-
-
+
+
-
-
+
+
-
+
+