mirror of
https://github.com/saymrwulf/prophet.git
synced 2026-07-05 04:17:56 +00:00
Fix bug about holiday year different from input year and others (#670)
* Allow both both hoidays and append holidays Match holidays in predict and fit Add test for append_holiday features; minor fixes Add column name validation for append_holidays names; allow only one country Fix bug about holiday year different from input year and also other bugs Change function description Add append holiday feature for R * Add test for R/Pyrhon; fix bugs
This commit is contained in:
parent
ad095ac690
commit
95fa4460a9
20 changed files with 67980 additions and 163 deletions
|
|
@ -32,7 +32,7 @@ Suggests:
|
|||
readr
|
||||
License: BSD_3_clause + file LICENSE
|
||||
LazyData: true
|
||||
RoxygenNote: 6.0.1
|
||||
RoxygenNote: 6.1.0
|
||||
VignetteBuilder: knitr
|
||||
SystemRequirements: C++11
|
||||
Encoding: UTF-8
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ export(add_seasonality)
|
|||
export(cross_validation)
|
||||
export(dyplot.prophet)
|
||||
export(fit.prophet)
|
||||
export(get_holiday_names)
|
||||
export(make_future_dataframe)
|
||||
export(make_holidays_df)
|
||||
export(performance_metrics)
|
||||
export(plot_cross_validation_metric)
|
||||
export(plot_forecast_component)
|
||||
|
|
|
|||
12
R/R/data.R
Normal file
12
R/R/data.R
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## Copyright (c) 2017-present, Facebook, Inc.
|
||||
## All rights reserved.
|
||||
|
||||
## This source code is licensed under the BSD-style license found in the
|
||||
## LICENSE file in the root directory of this source tree. An additional grant
|
||||
## of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
|
||||
#' holidays table
|
||||
#'
|
||||
#' @format A data frame with five variables: ds, holiday, country, year
|
||||
"generated_holidays"
|
||||
|
|
@ -154,6 +154,7 @@ prophet_copy <- function(m, cutoff = NULL) {
|
|||
weekly.seasonality = FALSE,
|
||||
daily.seasonality = FALSE,
|
||||
holidays = m$holidays,
|
||||
append.holidays = m$append.holidays,
|
||||
seasonality.mode = m$seasonality.mode,
|
||||
seasonality.prior.scale = m$seasonality.prior.scale,
|
||||
changepoint.prior.scale = m$changepoint.prior.scale,
|
||||
|
|
|
|||
46
R/R/make_holidays.R
Normal file
46
R/R/make_holidays.R
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
## Copyright (c) 2017-present, Facebook, Inc.
|
||||
## All rights reserved.
|
||||
|
||||
## This source code is licensed under the BSD-style license found in the
|
||||
## LICENSE file in the root directory of this source tree. An additional grant
|
||||
## of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
|
||||
#' Return all possible holiday names of given country
|
||||
#'
|
||||
#' @param country.name Country name (character).
|
||||
#'
|
||||
#' @return A vector of all possible holiday names (unique) of given country.
|
||||
#' @export
|
||||
get_holiday_names <- function(country.name){
|
||||
holidays <- generated_holidays %>%
|
||||
dplyr::filter(country == country.name) %>%
|
||||
dplyr::select(holiday) %>%
|
||||
unique()
|
||||
return(holidays$holiday)
|
||||
}
|
||||
|
||||
|
||||
#' Make dataframe of holidays for given years and countries
|
||||
#'
|
||||
#' @param country.name Country name (character).
|
||||
#'
|
||||
#' @return Dataframe with 'ds' and 'holiday', which can directly feed
|
||||
#' to 'holidays' params in Prophet
|
||||
#' @export
|
||||
make_holidays_df <- function(years, country.name){
|
||||
country.holidays = generated_holidays %>%
|
||||
dplyr::filter(country == country.name)
|
||||
max.year <- max(country.holidays$year)
|
||||
min.year <- min(country.holidays$year)
|
||||
if (max(years) > max.year || min(years) < min.year){
|
||||
warning.msg = paste("Holidays for", country.name, "are only supported from", min.year,
|
||||
"to", max.year)
|
||||
warning(warning.msg)
|
||||
}
|
||||
holidays.df <- country.holidays %>%
|
||||
dplyr::filter(year %in% years) %>%
|
||||
dplyr::select(ds, holiday) %>%
|
||||
data.frame
|
||||
return(holidays.df)
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
## Makes R CMD CHECK happy due to dplyr syntax below
|
||||
globalVariables(c(
|
||||
"ds", "y", "cap", ".",
|
||||
"component", "dow", "doy", "holiday", "holidays", "holidays_lower", "holidays_upper", "ix",
|
||||
"lower", "n", "stat", "trend", "row_number", "extra_regressors", "col",
|
||||
"component", "dow", "doy", "holiday", "holidays", "append.holidays", "holidays_lower",
|
||||
"holidays_upper", "ix", "lower", "n", "stat", "trend", "row_number", "extra_regressors", "col",
|
||||
"trend_lower", "trend_upper", "upper", "value", "weekly", "weekly_lower", "weekly_upper",
|
||||
"x", "yearly", "yearly_lower", "yearly_upper", "yhat", "yhat_lower", "yhat_upper"))
|
||||
|
||||
|
|
@ -43,6 +43,7 @@ globalVariables(c(
|
|||
#' range of days around the date to be included as holidays. lower_window=-2
|
||||
#' will include 2 days prior to the date as holidays. Also optionally can have
|
||||
#' a column prior_scale specifying the prior scale for each holiday.
|
||||
#' @param append.holidays country name or abbreviation (character).
|
||||
#' @param seasonality.mode 'additive' (default) or 'multiplicative'.
|
||||
#' @param seasonality.prior.scale Parameter modulating the strength of the
|
||||
#' seasonality model. Larger values allow the model to fit larger seasonal
|
||||
|
|
@ -87,6 +88,7 @@ prophet <- function(df = NULL,
|
|||
weekly.seasonality = 'auto',
|
||||
daily.seasonality = 'auto',
|
||||
holidays = NULL,
|
||||
append.holidays = NULL,
|
||||
seasonality.mode = 'additive',
|
||||
seasonality.prior.scale = 10,
|
||||
holidays.prior.scale = 10,
|
||||
|
|
@ -110,6 +112,7 @@ prophet <- function(df = NULL,
|
|||
weekly.seasonality = weekly.seasonality,
|
||||
daily.seasonality = daily.seasonality,
|
||||
holidays = holidays,
|
||||
append.holidays = append.holidays,
|
||||
seasonality.mode = seasonality.mode,
|
||||
seasonality.prior.scale = seasonality.prior.scale,
|
||||
changepoint.prior.scale = changepoint.prior.scale,
|
||||
|
|
@ -129,6 +132,7 @@ prophet <- function(df = NULL,
|
|||
params = list(),
|
||||
history = NULL,
|
||||
history.dates = NULL,
|
||||
train.holiday.names = NULL,
|
||||
train.component.cols = NULL,
|
||||
component.modes = NULL
|
||||
)
|
||||
|
|
@ -177,6 +181,11 @@ validate_inputs <- function(m) {
|
|||
validate_column_name(m, h, check_holidays = FALSE)
|
||||
}
|
||||
}
|
||||
if (!is.null(m$append.holidays)) {
|
||||
if (!(m$append.holidays %in% generated_holidays$country)){
|
||||
stop("Holidays in ", m$append.holidays," are not currently supported!")
|
||||
}
|
||||
}
|
||||
if (!(m$seasonality.mode %in% c('additive', 'multiplicative'))) {
|
||||
stop("seasonality.mode must be 'additive' or 'multiplicative'")
|
||||
}
|
||||
|
|
@ -214,6 +223,11 @@ validate_column_name <- function(
|
|||
(name %in% unique(m$holidays$holiday))){
|
||||
stop("Name ", name, " already used for a holiday.")
|
||||
}
|
||||
if(check_holidays & !is.null(m$append.holidays)){
|
||||
if(name %in% get_holiday_names(m$append.holidays)){
|
||||
stop("Name ", name, " is a holiday name in ", m$append.holidays, ".")
|
||||
}
|
||||
}
|
||||
if(check_seasonalities & (!is.null(m$seasonalities[[name]]))){
|
||||
stop("Name ", name, " already used for a seasonality.")
|
||||
}
|
||||
|
|
@ -534,10 +548,28 @@ make_seasonality_features <- function(dates, period, series.order, prefix) {
|
|||
make_holiday_features <- function(m, dates) {
|
||||
# Strip dates to be just days, for joining on holidays
|
||||
dates <- set_date(format(dates, "%Y-%m-%d"))
|
||||
wide <- m$holidays %>%
|
||||
all.holidays <- m$holidays
|
||||
if (!is.null(m$append.holidays)){
|
||||
years <- as.numeric(unique(format(dates, "%Y")))
|
||||
append.holidays.df <- make_holidays_df(years, m$append.holidays) %>%
|
||||
dplyr::mutate(ds=as.character(ds), holiday=as.character(holiday))
|
||||
all.holidays <- suppressWarnings(dplyr::bind_rows(all.holidays, append.holidays.df))
|
||||
}
|
||||
# Make fit.prophet and predict.prophet holidays components match
|
||||
if (!is.null(m$append.holidays) && !is.null(m$train.holiday.names)){
|
||||
row.to.keep <- which(all.holidays$holiday %in% m$train.holiday.names)
|
||||
all.holidays <- all.holidays[row.to.keep,]
|
||||
holidays.to.add <- data.frame(holiday=setdiff(m$train.holiday.names,
|
||||
all.holidays$holiday))
|
||||
all.holidays <- suppressWarnings(dplyr::bind_rows(all.holidays, holidays.to.add))
|
||||
}
|
||||
if (nrow(all.holidays)==0){
|
||||
return(NULL)
|
||||
}
|
||||
wide <- all.holidays %>%
|
||||
dplyr::mutate(ds = set_date(ds)) %>%
|
||||
dplyr::group_by(holiday, ds) %>%
|
||||
dplyr::filter(row_number() == 1) %>%
|
||||
dplyr::filter(dplyr::row_number() == 1) %>%
|
||||
dplyr::do({
|
||||
if (exists('lower_window', where = .) && !is.na(.$lower_window)
|
||||
&& !is.na(.$upper_window)) {
|
||||
|
|
@ -555,16 +587,17 @@ make_holiday_features <- function(m, dates) {
|
|||
holiday.features <- data.frame(ds = set_date(dates)) %>%
|
||||
dplyr::left_join(wide, by = 'ds') %>%
|
||||
dplyr::select(-ds)
|
||||
|
||||
# Make sure fit.prophet and predict.prophet component.cols perfectly equal
|
||||
holiday.features <- holiday.features %>% dplyr::select(sort(names(.)))
|
||||
holiday.features[is.na(holiday.features)] <- 0
|
||||
|
||||
|
||||
# Prior scales
|
||||
if (!('prior_scale' %in% colnames(m$holidays))) {
|
||||
m$holidays$prior_scale <- m$holidays.prior.scale
|
||||
if (!('prior_scale' %in% colnames(all.holidays))) {
|
||||
all.holidays$prior_scale <- m$holidays.prior.scale
|
||||
}
|
||||
prior.scales.list <- list()
|
||||
for (name in unique(m$holidays$holiday)) {
|
||||
df.h <- m$holidays[m$holidays$holiday == name, ]
|
||||
for (name in unique(all.holidays$holiday)) {
|
||||
df.h <- all.holidays[all.holidays$holiday == name, ]
|
||||
ps <- unique(df.h$prior_scale)
|
||||
if (length(ps) > 1) {
|
||||
stop('Holiday ', name, ' does not have a consistent prior scale ',
|
||||
|
|
@ -584,9 +617,14 @@ make_holiday_features <- function(m, dates) {
|
|||
sn <- strsplit(name, '_delim_', fixed = TRUE)[[1]][1]
|
||||
prior.scales <- c(prior.scales, prior.scales.list[[sn]])
|
||||
}
|
||||
return(list(holiday.features = holiday.features,
|
||||
holiday.names <- names(prior.scales.list)
|
||||
if (is.null(m$train.holiday.names)){
|
||||
m$train.holiday.names <- holiday.names
|
||||
}
|
||||
return(list(m = m,
|
||||
holiday.features = holiday.features,
|
||||
prior.scales = prior.scales,
|
||||
holiday.names = names(prior.scales.list)))
|
||||
holiday.names = holiday.names))
|
||||
}
|
||||
|
||||
#' Add an additional regressor to be used for fitting and predicting.
|
||||
|
|
@ -738,12 +776,15 @@ make_all_seasonality_features <- function(m, df) {
|
|||
}
|
||||
|
||||
# Holiday features
|
||||
if (!is.null(m$holidays)) {
|
||||
hf <- make_holiday_features(m, df$ds)
|
||||
seasonal.features <- cbind(seasonal.features, hf$holiday.features)
|
||||
prior.scales <- c(prior.scales, hf$prior.scales)
|
||||
modes[[m$seasonality.mode]] <- c(
|
||||
modes[[m$seasonality.mode]], hf$holiday.names)
|
||||
if (!is.null(m$holidays) || !is.null(m$append.holidays)) {
|
||||
out <- make_holiday_features(m, df$ds)
|
||||
if (!is.null(out)){
|
||||
m <- out$m
|
||||
seasonal.features <- cbind(seasonal.features, out$holiday.features)
|
||||
prior.scales <- c(prior.scales, out$prior.scales)
|
||||
modes[[m$seasonality.mode]] <- c(
|
||||
modes[[m$seasonality.mode]], out$holiday.names)
|
||||
}
|
||||
}
|
||||
|
||||
# Additional regressors
|
||||
|
|
@ -761,7 +802,8 @@ make_all_seasonality_features <- function(m, df) {
|
|||
}
|
||||
|
||||
components.list <- regressor_column_matrix(m, seasonal.features, modes)
|
||||
return(list(seasonal.features = seasonal.features,
|
||||
return(list(m = m,
|
||||
seasonal.features = seasonal.features,
|
||||
prior.scales = prior.scales,
|
||||
component.cols = components.list$component.cols,
|
||||
modes = components.list$modes))
|
||||
|
|
@ -792,9 +834,9 @@ regressor_column_matrix <- function(m, seasonal.features, modes) {
|
|||
extra = "merge", fill = "right") %>%
|
||||
dplyr::select(col, component)
|
||||
# Add total for holidays
|
||||
if(!is.null(m$holidays)){
|
||||
if(!is.null(m$train.holiday.names)){
|
||||
components <- add_group_component(
|
||||
components, 'holidays', unique(m$holidays$holiday))
|
||||
components, 'holidays', unique(m$train.holiday.names))
|
||||
}
|
||||
# Add totals for additive and multiplicative components, and regressors
|
||||
for (mode in c('additive', 'multiplicative')) {
|
||||
|
|
@ -1061,6 +1103,7 @@ fit.prophet <- function(m, df, ...) {
|
|||
m$history <- history
|
||||
m <- set_auto_seasonalities(m)
|
||||
out2 <- make_all_seasonality_features(m, history)
|
||||
m <- out2$m
|
||||
seasonal.features <- out2$seasonal.features
|
||||
prior.scales <- out2$prior.scales
|
||||
component.cols <- out2$component.cols
|
||||
|
|
@ -1136,7 +1179,7 @@ fit.prophet <- function(m, df, ...) {
|
|||
m$params <- stan.fit$par
|
||||
n.iteration <- 1
|
||||
}
|
||||
|
||||
|
||||
# Cast the parameters to have consistent form, whether full bayes or MAP
|
||||
for (name in c('delta', 'beta')){
|
||||
m$params[[name]] <- matrix(m$params[[name]], nrow = n.iteration)
|
||||
|
|
@ -1296,6 +1339,7 @@ predict_trend <- function(model, df) {
|
|||
#' @keywords internal
|
||||
predict_seasonal_components <- function(m, df) {
|
||||
out <- make_all_seasonality_features(m, df)
|
||||
m <- out$m
|
||||
seasonal.features <- out$seasonal.features
|
||||
component.cols <- out$component.cols
|
||||
lower.p <- (1 - m$interval.width)/2
|
||||
|
|
|
|||
10
R/data-raw/generated_holidays.R
Normal file
10
R/data-raw/generated_holidays.R
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
## Copyright (c) 2017-present, Facebook, Inc.
|
||||
## All rights reserved.
|
||||
|
||||
## This source code is licensed under the BSD-style license found in the
|
||||
## LICENSE file in the root directory of this source tree. An additional grant
|
||||
## of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
|
||||
generated_holidays <- read.csv("data-raw/generated_holidays.csv")
|
||||
devtools::use_data(generated_holidays, overwrite = TRUE)
|
||||
67401
R/data-raw/generated_holidays.csv
Normal file
67401
R/data-raw/generated_holidays.csv
Normal file
File diff suppressed because it is too large
Load diff
BIN
R/data/generated_holidays.rda
Normal file
BIN
R/data/generated_holidays.rda
Normal file
Binary file not shown.
14
R/man/generated_holidays.Rd
Normal file
14
R/man/generated_holidays.Rd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/data.R
|
||||
\docType{data}
|
||||
\name{generated_holidays}
|
||||
\alias{generated_holidays}
|
||||
\title{holidays table}
|
||||
\format{A data frame with five variables: ds, holiday, country, year}
|
||||
\usage{
|
||||
generated_holidays
|
||||
}
|
||||
\description{
|
||||
holidays table
|
||||
}
|
||||
\keyword{datasets}
|
||||
17
R/man/get_holiday_names.Rd
Normal file
17
R/man/get_holiday_names.Rd
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/make_holidays.R
|
||||
\name{get_holiday_names}
|
||||
\alias{get_holiday_names}
|
||||
\title{Return all possible holiday names of given country}
|
||||
\usage{
|
||||
get_holiday_names(country.name)
|
||||
}
|
||||
\arguments{
|
||||
\item{country.name}{Country name (character).}
|
||||
}
|
||||
\value{
|
||||
A vector of all possible holiday names (unique) of given country.
|
||||
}
|
||||
\description{
|
||||
Return all possible holiday names of given country
|
||||
}
|
||||
18
R/man/make_holidays_df.Rd
Normal file
18
R/man/make_holidays_df.Rd
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/make_holidays.R
|
||||
\name{make_holidays_df}
|
||||
\alias{make_holidays_df}
|
||||
\title{Make dataframe of holidays for given years and countries}
|
||||
\usage{
|
||||
make_holidays_df(years, country.name)
|
||||
}
|
||||
\arguments{
|
||||
\item{country.name}{Country name (character).}
|
||||
}
|
||||
\value{
|
||||
Dataframe with 'ds' and 'holiday', which can directly feed
|
||||
to 'holidays' params in Prophet
|
||||
}
|
||||
\description{
|
||||
Make dataframe of holidays for given years and countries
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
\alias{plot_forecast_component}
|
||||
\title{Plot a particular component of the forecast.}
|
||||
\usage{
|
||||
plot_forecast_component(m, fcst, name, uncertainty = TRUE, plot_cap = FALSE)
|
||||
plot_forecast_component(m, fcst, name, uncertainty = TRUE,
|
||||
plot_cap = FALSE)
|
||||
}
|
||||
\arguments{
|
||||
\item{m}{Prophet model}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ prophet(df = NULL, growth = "linear", changepoints = NULL,
|
|||
n.changepoints = 25, changepoint.range = 0.8,
|
||||
yearly.seasonality = "auto", weekly.seasonality = "auto",
|
||||
daily.seasonality = "auto", holidays = NULL,
|
||||
seasonality.mode = "additive", seasonality.prior.scale = 10,
|
||||
holidays.prior.scale = 10, changepoint.prior.scale = 0.05,
|
||||
mcmc.samples = 0, interval.width = 0.8, uncertainty.samples = 1000,
|
||||
fit = TRUE, ...)
|
||||
append.holidays = NULL, seasonality.mode = "additive",
|
||||
seasonality.prior.scale = 10, holidays.prior.scale = 10,
|
||||
changepoint.prior.scale = 0.05, mcmc.samples = 0,
|
||||
interval.width = 0.8, uncertainty.samples = 1000, fit = TRUE, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{df}{(optional) Dataframe containing the history. Must have columns ds
|
||||
|
|
@ -51,6 +51,8 @@ range of days around the date to be included as holidays. lower_window=-2
|
|||
will include 2 days prior to the date as holidays. Also optionally can have
|
||||
a column prior_scale specifying the prior scale for each holiday.}
|
||||
|
||||
\item{append.holidays}{country name or abbreviation (character).}
|
||||
|
||||
\item{seasonality.mode}{'additive' (default) or 'multiplicative'.}
|
||||
|
||||
\item{seasonality.prior.scale}{Parameter modulating the strength of the
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ test_that("copy", {
|
|||
weekly.seasonality = c(TRUE, FALSE),
|
||||
daily.seasonality = c(TRUE, FALSE),
|
||||
holidays = c('null', 'insert_dataframe'),
|
||||
append.holidays = c(NULL, 'US'),
|
||||
seasonality.mode = c('additive', 'multiplicative')
|
||||
)
|
||||
products <- expand.grid(inputs)
|
||||
|
|
@ -139,6 +140,7 @@ test_that("copy", {
|
|||
weekly.seasonality = products$weekly.seasonality[i],
|
||||
daily.seasonality = products$daily.seasonality[i],
|
||||
holidays = holidays,
|
||||
append.holidays = products$append.holidays[i],
|
||||
seasonality.prior.scale = 1.1,
|
||||
holidays.prior.scale = 1.1,
|
||||
changepoints.prior.scale = 0.1,
|
||||
|
|
@ -153,7 +155,7 @@ test_that("copy", {
|
|||
m1 <- prophet:::set_auto_seasonalities(m1)
|
||||
m2 <- prophet:::prophet_copy(m1)
|
||||
# Values should be copied correctly
|
||||
args <- c('growth', 'changepoints', 'n.changepoints', 'holidays',
|
||||
args <- c('growth', 'changepoints', 'n.changepoints', 'holidays', 'append.holidays',
|
||||
'seasonality.prior.scale', 'holidays.prior.scale',
|
||||
'changepoints.prior.scale', 'mcmc.samples', 'interval.width',
|
||||
'uncertainty.samples', 'seasonality.mode', 'changepoint.range')
|
||||
|
|
|
|||
|
|
@ -349,6 +349,49 @@ test_that("fit_with_holidays", {
|
|||
expect_error(predict(m), NA)
|
||||
})
|
||||
|
||||
test_that("fit_with_append_holidays", {
|
||||
skip_if_not(Sys.getenv('R_ARCH') != '/i386')
|
||||
holidays <- data.frame(ds = c('2012-06-06', '2013-06-06'),
|
||||
holiday = c('seans-bday', 'seans-bday'),
|
||||
lower_window = c(0, 0),
|
||||
upper_window = c(1, 1))
|
||||
append.holidays = 'US'
|
||||
# Test with holidays and append_holidays
|
||||
m <- prophet(DATA,
|
||||
holidays = holidays,
|
||||
append.holidays = append.holidays,
|
||||
uncertainty.samples = 0)
|
||||
expect_error(predict(m), NA)
|
||||
# There are training holidays missing in the test set
|
||||
train2 <- DATA %>% head(155)
|
||||
future2 <- DATA %>% tail(355)
|
||||
model <- prophet(train2,
|
||||
append.holidays = append.holidays,
|
||||
uncertainty.samples = 0)
|
||||
expect_error(predict(m, future2), NA)
|
||||
# There are test holidays missing in the training set
|
||||
train2 <- DATA %>% tail(355)
|
||||
future2 <- DATA2
|
||||
model <- prophet(train2,
|
||||
append.holidays = append.holidays,
|
||||
uncertainty.samples = 0)
|
||||
expect_error(predict(m, future2), NA)
|
||||
# Append_holidays with non-existing year
|
||||
max.year <- generated_holidays %>%
|
||||
dplyr::filter(country==append.holidays) %>%
|
||||
dplyr::select(year) %>%
|
||||
max()
|
||||
train2 <- data.frame('ds'=c(paste(max.year+1, "-01-01", sep=''),
|
||||
paste(max.year+1, "-01-02", sep='')),
|
||||
'y'=1)
|
||||
expect_warning(prophet(train2,
|
||||
append.holidays = append.holidays))
|
||||
# Append_holidays with non-existing country
|
||||
append.holidays = 'Utopia'
|
||||
expect_error(prophet(DATA,
|
||||
append.holidays = append.holidays))
|
||||
})
|
||||
|
||||
test_that("make_future_dataframe", {
|
||||
skip_if_not(Sys.getenv('R_ARCH') != '/i386')
|
||||
train.t <- DATA[1:234, ]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from datetime import date, timedelta
|
|||
from convertdate.islamic import to_gregorian, from_gregorian
|
||||
import warnings
|
||||
|
||||
|
||||
# Official public holidays at a country level
|
||||
# ------------ Holidays in Brazil---------------------
|
||||
class Brazil(HolidayBase):
|
||||
|
|
@ -89,7 +90,10 @@ class Indonesia(HolidayBase):
|
|||
|
||||
# Chinese New Year/ Spring Festival
|
||||
name = "Chinese New Year"
|
||||
self[LunarDate(year, 1, 1).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 1, 1).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Day of Silence / Nyepi
|
||||
# Note:
|
||||
|
|
@ -126,10 +130,12 @@ class Indonesia(HolidayBase):
|
|||
pass
|
||||
|
||||
# Ascension of the Prophet
|
||||
islam_year = from_gregorian(year, 3, 17)[0]
|
||||
y, m, d = to_gregorian(islam_year, 7, 27)
|
||||
name = "Ascension of the Prophet"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 3, 17)[0]
|
||||
y, m, d = to_gregorian(islam_year, 7, 27)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Labor Day
|
||||
name = "Labor Day"
|
||||
|
|
@ -137,12 +143,17 @@ class Indonesia(HolidayBase):
|
|||
|
||||
# Ascension of Jesus Christ
|
||||
name = "Ascension of Jesus"
|
||||
hdate = easter(year) + rd(days=+39)
|
||||
self[hdate] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) + rd(days=+39)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Buddha's Birthday
|
||||
name = "Buddha's Birthday"
|
||||
self[LunarDate(year, 4, 15).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 4, 15).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Pancasila Day, since 2017
|
||||
if year >= 2017:
|
||||
|
|
@ -150,34 +161,43 @@ class Indonesia(HolidayBase):
|
|||
self[date(year, 6, 1)] = name
|
||||
|
||||
# Eid al-Fitr
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
name = "Eid al-Fitr"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
if y1 == year:
|
||||
self[date(y1, m2, d2)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
|
||||
# Independence Day
|
||||
name = "Independence Day"
|
||||
self[date(year, 8, 17)] = name
|
||||
|
||||
# Feast of the Sacrifice
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
name = "Feast of the Sacrifice"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Islamic New Year
|
||||
islam_year = from_gregorian(year, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
name = "Islamic New Year"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Birth of the Prophet
|
||||
islam_year = from_gregorian(year, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 3, 12)
|
||||
name = "Birth of the Prophet"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 3, 12)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Christmas
|
||||
self[date(year, 12, 25)] = "Christmas"
|
||||
|
|
@ -298,32 +318,41 @@ class India(HolidayBase):
|
|||
|
||||
# Day of Ashura
|
||||
# 10th day of 1st Islamic month
|
||||
islam_year = from_gregorian(year, 10, 1)[0]
|
||||
y, m, d = to_gregorian(islam_year, 1, 10)
|
||||
name = "Day of Ashura"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 10, 1)[0]
|
||||
y, m, d = to_gregorian(islam_year, 1, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Mawlid, Birth of the Prophet
|
||||
# 12th day of 3rd Islamic month
|
||||
islam_year = from_gregorian(year, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
name = "Mawlid"
|
||||
self[date(y, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Eid ul-Fitr
|
||||
# 1st and 2nd day of 10th Islamic month
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
name = "Eid al-Fitr"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
|
||||
# Eid al-Adha, i.e., Feast of the Sacrifice
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
name = "Feast of the Sacrifice"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# --------------------------------
|
||||
# Christian holidays
|
||||
|
|
@ -345,23 +374,38 @@ class India(HolidayBase):
|
|||
|
||||
# Palm Sunday
|
||||
name = "Palm Sunday"
|
||||
self[easter(year) - rd(days=7)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=7)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Maundy Thursday
|
||||
name = "Maundy Thursday"
|
||||
self[easter(year) - rd(days=3)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=3)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Good Friday
|
||||
name = "Good Friday"
|
||||
self[easter(year) - rd(days=2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=2)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Easter Sunday
|
||||
name = "Easter Sunday"
|
||||
self[easter(year)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Feast of Pentecost
|
||||
name = "Feast of Pentecost"
|
||||
self[easter(year) + rd(days=49)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) + rd(days=49)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Fest of St. Theresa of Calcutta
|
||||
name = "Fest of St. Theresa of Calcutta"
|
||||
|
|
@ -416,14 +460,19 @@ class Malaysia(HolidayBase):
|
|||
|
||||
# Birthday of Prophet, Mawlid in India
|
||||
# 12th day of 3rd Islamic month
|
||||
islam_year = from_gregorian(year, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
name = "Birth of Prophet"
|
||||
self[date(y, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Chinese New Year
|
||||
name = "Chinese New Year"
|
||||
self[LunarDate(year, 1, 1).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 1, 1).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Tamil New Year
|
||||
# Note: it's not necessarily 04/14
|
||||
|
|
@ -434,7 +483,10 @@ class Malaysia(HolidayBase):
|
|||
|
||||
# Good Friday
|
||||
name = "Good Friday"
|
||||
self[easter(year) - rd(days=2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=2)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Labor Day
|
||||
name = "Labor Day"
|
||||
|
|
@ -442,7 +494,10 @@ class Malaysia(HolidayBase):
|
|||
|
||||
# Buddha's Birthday
|
||||
name = "Wesak Day"
|
||||
self[LunarDate(year, 4, 15).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 4, 15).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# King's birthday
|
||||
# https://www.thestar.com.my/news/nation/2017/04/26/
|
||||
|
|
@ -471,28 +526,35 @@ class Malaysia(HolidayBase):
|
|||
self[saturdays[-1]] = name
|
||||
|
||||
# Eid al-Fitr
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
name = "Eid al-Fitr"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
|
||||
# Malaysia Day
|
||||
name = "Malaysia Day"
|
||||
self[date(year, 9, 16)] = name
|
||||
|
||||
# Feast of the Sacrifice
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
name = "Feast of the Sacrifice"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# First Day of Muharram
|
||||
islam_year = from_gregorian(year, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
name = "First Day of Muharram"
|
||||
self[date(y, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Christmas
|
||||
name = "Christmas Day"
|
||||
|
|
@ -523,16 +585,35 @@ class Vietnam(HolidayBase):
|
|||
|
||||
# Vietnamese New Year
|
||||
name = "Vietnamese New Year"
|
||||
self[LunarDate(year - 1, 12, 30).toSolarDate()] = name
|
||||
self[LunarDate(year, 1, 1).toSolarDate()] = name
|
||||
self[LunarDate(year, 1, 2).toSolarDate()] = name
|
||||
self[LunarDate(year, 1, 3).toSolarDate()] = name
|
||||
self[LunarDate(year, 1, 4).toSolarDate()] = name
|
||||
self[LunarDate(year, 1, 5).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
try:
|
||||
ds = LunarDate(year - 1 + offset, 12, 30).toSolarDate()
|
||||
except ValueError:
|
||||
ds = LunarDate(year - 1 + offset, 12, 29).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
ds = LunarDate(year + offset, 1, 1).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
ds = LunarDate(year + offset, 1, 2).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
ds = LunarDate(year + offset, 1, 3).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
ds = LunarDate(year + offset, 1, 4).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
ds = LunarDate(year + offset, 1, 5).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Hung Kings Commemorations
|
||||
name = "Hung Kings Commemorations"
|
||||
self[LunarDate(year, 3, 10).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 3, 10).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Reunification Day
|
||||
name = "Reunification Day"
|
||||
|
|
@ -606,7 +687,10 @@ class Thailand(HolidayBase):
|
|||
|
||||
# Buddha's Birthday
|
||||
name = "Buddha's Birthday"
|
||||
self[LunarDate(year, 4, 15).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 4, 15).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Coronation Day, removed in 2017
|
||||
name = "Coronation Day"
|
||||
|
|
@ -754,11 +838,17 @@ class Philippines(HolidayBase):
|
|||
|
||||
# Maundy Thursday
|
||||
name = "Maundy Thursday"
|
||||
self[easter(year) - rd(days=3)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=3)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Good Friday
|
||||
name = "Good Friday"
|
||||
self[easter(year) - rd(days=2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = easter(year + offset) - rd(days=2)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Day of Valor
|
||||
name = "Day of Valor"
|
||||
|
|
@ -774,15 +864,20 @@ class Philippines(HolidayBase):
|
|||
|
||||
# Eid al-Fitr
|
||||
name = "Eid al-Fitr"
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
self[date(year, m1, d1) - timedelta(days=1)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y, m, d = to_gregorian(islam_year, 10, 1)
|
||||
ds = date(y, m, d) - timedelta(days=1)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Eid al-Adha, i.e., Feast of the Sacrifice
|
||||
name = "Feast of the Sacrifice"
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# National Heroes' Day
|
||||
name = "National Heroes' Day"
|
||||
|
|
@ -849,15 +944,20 @@ class Turkey(HolidayBase):
|
|||
|
||||
# Eid al-Fitr
|
||||
name = "Eid al-Fitr"
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
self[date(year, m1, d1) - timedelta(days=1)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y, m, d = to_gregorian(islam_year, 10, 1)
|
||||
ds = date(y, m, d) - timedelta(days=1)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Eid al-Adha, i.e., Feast of the Sacrifice
|
||||
name = "Feast of the Sacrifice"
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y, m, d = to_gregorian(islam_year, 12, 10)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
|
||||
class TU(Turkey):
|
||||
|
|
@ -906,45 +1006,60 @@ class Pakistan(HolidayBase):
|
|||
|
||||
# Eid al-Adha, i.e., Feast of the Sacrifice
|
||||
name = "Feast of the Sacrifice"
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 12, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 12, 11)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 12, 12)
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
self[date(year, m3, d3)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 12, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 12, 11)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 12, 12)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
if y3 == year:
|
||||
self[date(y3, m3, d3)] = name
|
||||
|
||||
# Eid al-Fitr
|
||||
name = "Eid al-Fitr"
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 10, 3)
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
self[date(year, m3, d3)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 10, 3)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
if y3 == year:
|
||||
self[date(y3, m3, d3)] = name
|
||||
|
||||
# Mawlid, Birth of the Prophet
|
||||
# 12th day of 3rd Islamic month
|
||||
islam_year = from_gregorian(year, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
name = "Mawlid"
|
||||
self[date(y, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Day of Ashura
|
||||
# 10th and 11th days of 1st Islamic month
|
||||
islam_year = from_gregorian(year, 10, 1)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 1, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 1, 11)
|
||||
name = "Day of Ashura"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 10, 1)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 1, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 1, 11)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
|
||||
# Shab e Mairaj
|
||||
islam_year = from_gregorian(year, 4, 13)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 7, 27)
|
||||
name = "Shab e Mairaj"
|
||||
self[date(year, m1, d1)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 4, 13)[0]
|
||||
y, m, d = to_gregorian(islam_year, 7, 27)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Defence Day
|
||||
name = "Defence Day"
|
||||
|
|
@ -1052,44 +1167,60 @@ class Egypt(HolidayBase):
|
|||
# Sham El Nessim
|
||||
# The Monday following Orthodox Easter
|
||||
name = "Sham El Nessim"
|
||||
orthodox_easter = easter(year, method=2)
|
||||
self[orthodox_easter + timedelta(days=1)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
orthodox_easter = easter(year + offset, method=2)
|
||||
ds = orthodox_easter + timedelta(days=1)
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Islamic New Year
|
||||
islam_year = from_gregorian(year, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
name = "Islamic New Year"
|
||||
self[date(year, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 9, 11)[0]
|
||||
y, m, d = to_gregorian(islam_year + 1, 1, 1)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Birthday of Prophet, Mawlid in India
|
||||
# 12th day of 3rd Islamic month
|
||||
islam_year = from_gregorian(year, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
name = "Birth of Prophet"
|
||||
self[date(y, m, d)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 11, 20)[0]
|
||||
y, m, d = to_gregorian(islam_year, 3, 12)
|
||||
if y == year:
|
||||
self[date(y, m, d)] = name
|
||||
|
||||
# Eid ul-Fitr
|
||||
# 1st and 2nd day of 10th Islamic month
|
||||
islam_year = from_gregorian(year, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 10, 3)
|
||||
name = "Eid al-Fitr"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
self[date(year, m3, d3)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 6, 15)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 10, 1)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 10, 2)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 10, 3)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
if y3 == year:
|
||||
self[date(y3, m3, d3)] = name
|
||||
|
||||
# Eid al-Adha, i.e., Feast of the Sacrifice
|
||||
islam_year = from_gregorian(year, 8, 22)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 12, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 12, 11)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 12, 12)
|
||||
y4, m4, d4 = to_gregorian(islam_year, 12, 13)
|
||||
name = "Feast of the Sacrifice"
|
||||
self[date(year, m1, d1)] = name
|
||||
self[date(year, m2, d2)] = name
|
||||
self[date(year, m3, d3)] = name
|
||||
self[date(year, m4, d4)] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
islam_year = from_gregorian(year + offset, 8, 22)[0]
|
||||
y1, m1, d1 = to_gregorian(islam_year, 12, 10)
|
||||
y2, m2, d2 = to_gregorian(islam_year, 12, 11)
|
||||
y3, m3, d3 = to_gregorian(islam_year, 12, 12)
|
||||
y4, m4, d4 = to_gregorian(islam_year, 12, 13)
|
||||
if y1 == year:
|
||||
self[date(y1, m1, d1)] = name
|
||||
if y2 == year:
|
||||
self[date(y2, m2, d2)] = name
|
||||
if y3 == year:
|
||||
self[date(y3, m3, d3)] = name
|
||||
if y4 == year:
|
||||
self[date(y4, m4, d4)] = name
|
||||
|
||||
|
||||
class EG(Egypt):
|
||||
|
|
@ -1116,7 +1247,10 @@ class China(HolidayBase):
|
|||
|
||||
# Chinese New Year/ Spring Festival
|
||||
name = "Chinese New Year"
|
||||
self[LunarDate(year, 1, 1).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 1, 1).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Tomb-Sweeping Day
|
||||
name = "Tomb-Sweeping Day"
|
||||
|
|
@ -1129,11 +1263,17 @@ class China(HolidayBase):
|
|||
|
||||
# Dragon Boat Festival
|
||||
name = "Dragon Boat Festival"
|
||||
self[LunarDate(year, 5, 5).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 5, 5).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# Mid-Autumn Festival
|
||||
name = "Mid-Autumn Festival"
|
||||
self[LunarDate(year, 8, 15).toSolarDate()] = name
|
||||
for offset in range(-1, 2, 1):
|
||||
ds = LunarDate(year + offset, 8, 15).toSolarDate()
|
||||
if ds.year == year:
|
||||
self[ds] = name
|
||||
|
||||
# National Day
|
||||
name = "National Day"
|
||||
|
|
|
|||
|
|
@ -25,9 +25,8 @@ def get_holiday_names(country):
|
|||
country: country name
|
||||
|
||||
Returns
|
||||
------- a
|
||||
Dataframe with 'ds' and 'holiday', which can directly feed
|
||||
to 'holidays' params in Prophet
|
||||
-------
|
||||
A set of all possible holiday names of given country
|
||||
"""
|
||||
years = np.arange(1995, 2045)
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ class TestDiagnostics(TestCase):
|
|||
# These values are created except for its default values
|
||||
holiday = pd.DataFrame(
|
||||
{'ds': pd.to_datetime(['2016-12-25']), 'holiday': ['x']})
|
||||
append_holidays = 'US'
|
||||
products = itertools.product(
|
||||
['linear', 'logistic'], # growth
|
||||
[None, pd.to_datetime(['2016-12-25'])], # changepoints
|
||||
|
|
@ -150,6 +151,7 @@ class TestDiagnostics(TestCase):
|
|||
[True, False], # weekly_seasonality
|
||||
[True, False], # daily_seasonality
|
||||
[None, holiday], # holidays
|
||||
[None, append_holidays], # append_holidays
|
||||
['additive', 'multiplicative'], # seasonality_mode
|
||||
[1.1], # seasonality_prior_scale
|
||||
[1.1], # holidays_prior_scale
|
||||
|
|
@ -182,6 +184,7 @@ class TestDiagnostics(TestCase):
|
|||
self.assertEqual(m1.holidays, m2.holidays)
|
||||
else:
|
||||
self.assertTrue((m1.holidays == m2.holidays).values.all())
|
||||
self.assertEqual(m1.append_holidays, m2.append_holidays)
|
||||
self.assertEqual(m1.seasonality_mode, m2.seasonality_mode)
|
||||
self.assertEqual(m1.seasonality_prior_scale, m2.seasonality_prior_scale)
|
||||
self.assertEqual(m1.changepoint_prior_scale, m2.changepoint_prior_scale)
|
||||
|
|
|
|||
62
python/scripts/generate_holidays_file.py
Normal file
62
python/scripts/generate_holidays_file.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Copyright (c) 2017-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import warnings
|
||||
import holidays as hdays_part1
|
||||
import fbprophet.hdays as hdays_part2
|
||||
import inspect
|
||||
|
||||
|
||||
def generate_holidays_file():
|
||||
"""Generate csv file of all possible holiday names, ds,
|
||||
and countries, year combination
|
||||
"""
|
||||
years = np.arange(1995, 2045, 1)
|
||||
all_holidays = []
|
||||
# class names in holiday packages which are not countries
|
||||
class_to_exclude = set(['rd', 'datetime', 'date', 'HolidayBase', 'Calendar',
|
||||
'LunarDate', 'timedelta', 'date'])
|
||||
|
||||
class_list2 = inspect.getmembers(hdays_part2, inspect.isclass)
|
||||
country_set2 = set(list(zip(*class_list2))[0])
|
||||
country_set2 -= class_to_exclude
|
||||
for country in country_set2:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
temp = getattr(hdays_part2, country)(years=years)
|
||||
temp_df = pd.DataFrame(list(temp.items()),
|
||||
columns=['ds', 'holiday'])
|
||||
temp_df['country'] = country
|
||||
all_holidays.append(temp_df)
|
||||
|
||||
class_list1 = inspect.getmembers(hdays_part1, inspect.isclass)
|
||||
country_set1 = set(list(zip(*class_list1))[0])
|
||||
country_set1 -= class_to_exclude
|
||||
# Avoid overwrting holidays get from hdays_part2
|
||||
country_set1 -= country_set2
|
||||
for country in country_set1:
|
||||
temp = getattr(hdays_part1, country)(years=years)
|
||||
temp_df = pd.DataFrame(list(temp.items()),
|
||||
columns=['ds', 'holiday'])
|
||||
temp_df['country'] = country
|
||||
all_holidays.append(temp_df)
|
||||
|
||||
generated_holidays = pd.concat(all_holidays, axis=0, ignore_index=True)
|
||||
generated_holidays['year'] = generated_holidays.ds.apply(lambda x: x.year)
|
||||
generated_holidays.to_csv("../R/data-raw/generated_holidays.csv")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# execute only if run as a script
|
||||
generate_holidays_file()
|
||||
Loading…
Reference in a new issue