```{css, echo=FALSE}
/Setting for scrollable code/text/
pre { max-height: 400px; overflow-y: auto; }
pre[class] { max-height: 400px; }
/Setting for TOC and outlook/
div.main-container { max-width: 100% !important; }
.tocify { max-width: 20% !important; }
.toc-content { padding-left: 5px !important; }
```r # General packages library(tidyverse) library(gridExtra) library(DT) library(plotly) library(trelliscopejs) library(glue) library(formattable) library(gganimate) library(future) # Special packages # Time series manipulation library(tsibble) # Forecasting functions library(fable) # Time series graphics and statistics library(feasts) # Prophet modeling interface for fable library(fable.prophet) # My packages devtools::load_all() # Set up future plan for parallel process if (require("future", quietly = TRUE)) { old_future_plan <- future::plan() future::plan(future::multisession) } # Set global options for knitr knitr::opts_chunk$set( fig.align = "center", fig.show = "hold", message = FALSE, warning = FALSE, collapse = TRUE, results = "hold", comment = "#>", out.width = "80%", tidy = "styler" )
focus_stknames <- params$focus_stocks focus_stkcds <- zstexplorer::name2code(params$focus_stocks) focus_var_codes <- params$focus_vars focus_var_names <- zstexplorer::code2name(params$focus_vars, exact_match = TRUE) tsbl_vars <- load_tsbl_vars(use_online_data = TRUE) # Build time series of focused stocks tsbl_focus_stock_raw <- tsbl_vars %>% tsibble::filter_index(format(params$start_date) ~ format(params$end_date)) %>% dplyr::filter(stkcd %in% focus_stkcds) %>% dplyr::mutate( date = tsibble::yearquarter(date), stkname = zstexplorer::code2name(stkcd) ) %>% dplyr::select( c("date", "stkcd", "stkname", "indcd"), tidyselect::all_of(focus_var_codes), -c("period") ) %>% tsibble::update_tsibble(key = "stkcd") # Build time series of focused industry tsbl_vars_industry_median <- tsbl_vars %>% dplyr::group_by(.data$indcd) %>% dplyr::summarise( dplyr::across(where(is.numeric), ~ median(., na.rm = TRUE)) ) tsbl_vars_industry_mean <- tsbl_vars %>% dplyr::group_by(.data$indcd) %>% dplyr::summarise( dplyr::across(where(is.numeric), ~ mean(., na.rm = TRUE)) ) tsbl_vars_industry <- tsbl_vars_industry_median focus_indcds <- unique(tsbl_focus_stock_raw$indcd) focus_indnames <- zstexplorer::code2name(focus_indcds) tsbl_focus_industry_raw <- tsbl_vars_industry %>% tsibble::filter_index(format(params$start_date) ~ format(params$end_date)) %>% dplyr::filter(.data$indcd %in% focus_indcds) %>% dplyr::mutate( date = tsibble::yearquarter(date), indname = zstexplorer::code2name(indcd) ) %>% dplyr::select( c("date", "indcd", "indname"), tidyselect::all_of(focus_var_codes) ) %>% tsibble::update_tsibble(key = "indcd")
Start date : r format(params$start_date)
End date : r format(params$end_date)
Focus stocks: r knitr::combine_words(glue::glue("{focus_stknames}({focus_stkcds})"))
Focus industries: r knitr::combine_words(glue::glue("{focus_indnames}({focus_indcds})"))
Focus variables: r knitr::combine_words(glue::glue("{focus_var_codes}({focus_var_names})"))
full_gap <- FALSE
# Has gaps for focused stocks tsbl_focus_stock_has_gaps <- tsibble::has_gaps( tsbl_focus_stock_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_stock_has_gaps, format = "html", caption = "Gaps(Implict Missingnes)in focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Has gaps for focused industries tsbl_focus_industry_has_gaps <- tsibble::has_gaps( tsbl_focus_industry_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_industry_has_gaps, format = "html", caption = "Gaps(Implict Missingnes)in Focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Scan gaps for focused stocks tsbl_focus_stock_scan_gaps <- tsibble::scan_gaps( tsbl_focus_stock_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_stock_scan_gaps, format = "html", caption = "Gaps(Implict Missingnes)in focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Scan gaps for focused industries tsbl_focus_industry_scan_gaps <- tsibble::scan_gaps( tsbl_focus_industry_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_industry_scan_gaps, format = "html", caption = "Gaps(Implict Missingnes)in focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Count gaps for focused stocks tsbl_focus_stock_count_gaps <- tsibble::count_gaps( tsbl_focus_stock_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_stock_count_gaps, format = "html", caption = "Gaps(Implict Missingnes)in focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Count gaps for focused industries tsbl_focus_industry_count_gaps <- tsibble::count_gaps( tsbl_focus_industry_raw, .full = !!full_gap ) knitr::kable(tsbl_focus_industry_count_gaps, format = "html", caption = "Gaps(Implict Missingnes)in focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Plot gaps for focused stocks tsbl_focus_stock_gaps <- tsibble::count_gaps( tsbl_focus_stock_raw, .full = !!full_gap ) ggplot(tsbl_focus_stock_gaps, aes(x = stkcd, colour = stkcd)) + geom_linerange(aes(ymin = .from, ymax = .to)) + geom_point(aes(y = .from)) + geom_point(aes(y = .to)) + coord_flip() + theme(legend.position = "bottom") + labs(title = "Gaps(Implict Missingnes)in focused stocks") # Plot gaps for focused industries tsbl_focus_industry_gaps <- tsibble::count_gaps( tsbl_focus_industry_raw, .full = !!full_gap ) ggplot(tsbl_focus_industry_gaps, aes(x = indcd, colour = indcd)) + geom_linerange(aes(ymin = .from, ymax = .to)) + geom_point(aes(y = .from)) + geom_point(aes(y = .to)) + coord_flip() + theme(legend.position = "bottom") + labs(title = "Gaps(Implict Missingnes)in focused industries")
tsbl_focus_stock_raw %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused stocks") tsbl_focus_industry_raw %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused industry")
# Fill gaps with NAs for focused stocks tsbl_focus_stock <- tsbl_focus_stock_raw %>% tsibble::group_by_key() %>% tsibble::fill_gaps(.full = !!full_gap) %>% dplyr::mutate(stkname = zstexplorer::code2name(stkcd)) tsbl_focus_stock %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused stocks") # Fill gaps with NAs for focused industries tsbl_focus_industry <- tsbl_focus_industry_raw %>% tsibble::group_by_key() %>% tsibble::fill_gaps(.full = !!full_gap) %>% dplyr::mutate(indname = zstexplorer::code2name(indcd)) tsbl_focus_industry %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused industries")
# Fill NA with previous values for focused stocks tsbl_focus_stock <- tsbl_focus_stock %>% tidyr::fill(tidyselect::everything(), .direction = "down") %>% dplyr::ungroup() %>% tsibble::as_tsibble(key = c(stkcd, indcd)) tsbl_focus_stock %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused stocks") # Fill NA with previous values for focused industries tsbl_focus_industry <- tsbl_focus_industry %>% tidyr::fill(tidyselect::everything(), .direction = "down") %>% dplyr::ungroup() %>% tsibble::as_tsibble(key = c(indcd)) tsbl_focus_industry %>% tibble::as_tibble() %>% visdat::vis_dat() + ggplot2::labs(title = "Missing values in data", subtitle = "Focused industries")
# Combine variables of focused stock and focused industry into # various format of tsibble for latter using. # Long format of tsbl_focus long_tsbl_focus_stock <- tsbl_focus_stock %>% tidyr::pivot_longer( cols = -c("date", "stkcd", "stkname", "indcd"), names_to = "variable", values_to = "value" ) long_tsbl_focus_industry <- tsbl_focus_industry %>% tidyr::pivot_longer( cols = -c("date", "indcd", "indname"), names_to = "variable", values_to = "value" ) long_tsbl_focus <- long_tsbl_focus_stock %>% dplyr::left_join( long_tsbl_focus_industry, by = c("date", "indcd", "variable"), suffix = c("_stock", "_industry") ) %>% dplyr::select(c( date, stkcd, stkname, indcd, indname, variable, value_stock, value_industry )) %>% tsibble::as_tsibble(key = c(stkcd, indcd, variable)) # Nested format of tsbl_focus nest_tsbl_focus_stock <- long_tsbl_focus_stock %>% tidyr::nest(tsbl_variable = c("date", "value")) %>% dplyr::ungroup() nest_tsbl_focus_industry <- long_tsbl_focus_industry %>% tidyr::nest(tsbl_variable = c("date", "value")) %>% dplyr::ungroup() nest_tsbl_focus <- nest_tsbl_focus_stock %>% dplyr::left_join(nest_tsbl_focus_industry, by = c("indcd", "variable"), suffix = c("_stock", "_industry") ) nest_tsbl_focus <- nest_tsbl_focus %>% dplyr::rowwise() %>% dplyr::mutate( tsbl_variable = list(dplyr::left_join( tsbl_variable_stock, tsbl_variable_industry, by = c("date"), suffix = c("_stock", "_industry") )) ) %>% dplyr::select( c("stkcd", "stkname", "indcd", "indname", "variable", "tsbl_variable"), -c("tsbl_variable_stock", "tsbl_variable_industry") ) %>% dplyr::ungroup()
timeseries_plot <- function(tsbl_variable, value_variable, reference_variable) { tsbl_variable %>% fabletools::autoplot(.vars = .data[[value_variable]]) + fabletools::autolayer(tsbl_variable, .data[[reference_variable]], color = "blue", linetype = "longdash" ) } # Plot time sereies of focused stocks nest_tsbl_focus_timeseries <- nest_tsbl_focus %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, timeseries_plot, value_variable = "value_stock", reference_variable = "value_industry" ) ) nest_tsbl_focus_timeseries %>% dplyr::select(variable, indcd, stkcd, panel_plot) %>% trelliscopejs::trelliscope( name = "time_series_stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/timeseries/stocks" )
# Notice: can't put following code into previous chunck, # because it will cause error in trelliscope plot for unknown reasons # Plot time series of focused industries nest_tsbl_focus_industry_timeseries <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, timeseries_plot, value_variable = "value", reference_variable = "value" ) ) nest_tsbl_focus_industry_timeseries %>% dplyr::select(variable, indcd, panel_plot) %>% trelliscopejs::trelliscope( name = "time_series_industries", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/timeseries/industries" )
long_tsbl_focus %>% fabletools::autoplot(value_stock, aes(color = stkname)) + fabletools::autolayer(long_tsbl_focus, value_industry, linetype = "longdash") + tsibble::scale_x_yearquarter() + labs(y = "value") + facet_grid(variable ~ indcd + stkcd + stkname, scales = "free") + theme(legend.position = "none") long_tsbl_focus %>% ggplot(aes(x = date)) + geom_line(aes(y = value_stock, color = stkname)) + geom_line(aes(y = value_industry), linetype = "longdash") + tsibble::scale_x_yearquarter() + labs(y = "value") + facet_grid(variable ~ indcd + indname, scales = "free") + theme_light()
gg_season_plot <- function(tsbl_variable, variable) { tsbl_variable %>% feasts::gg_season(y = .data[[variable]]) } # Plot gg_season for focused stocks nest_tsbl_focus_stock_season <- nest_tsbl_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, gg_season_plot, variable = "value" ) ) nest_tsbl_focus_stock_season %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "gg_season_stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/gg_season/stocks" ) # Plot gg_season for focused industries nest_tsbl_focus_industry_season <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, gg_season_plot, variable = "value" ) ) nest_tsbl_focus_industry_season %>% dplyr::select(c("variable", "indcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "gg_season_industries", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/gg_season/industries" )
long_tsbl_focus_stock %>% feasts::gg_season(value) + facet_grid(variable ~ indcd + stkcd + stkname, scales = "free_y") long_tsbl_focus_industry %>% feasts::gg_season(value) + facet_grid(variable ~ indcd + indname, scales = "free_y")
gg_subseries_plot <- function(tsbl_variable, variable) { tsbl_variable %>% feasts::gg_subseries(y = .data[[variable]]) } # Plot gg_subseries for focused stocks nest_tsbl_focus_stock_subseries <- nest_tsbl_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, gg_subseries_plot, variable = "value" ) ) nest_tsbl_focus_stock_subseries %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "gg_subseries_stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/gg_subseries/stocks" ) # Plot gg_subseries for focused industries nest_tsbl_focus_industry_subseries <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, gg_subseries_plot, variable = "value" ) ) nest_tsbl_focus_industry_subseries %>% dplyr::select(c("variable", "indcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "gg_subseries_industries", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/gg_subseries/industries" )
long_tsbl_focus_stock %>% feasts::gg_subseries(value) + facet_grid(variable ~ indcd + stkcd + stkname, scales = "free_y") long_tsbl_focus_industry %>% feasts::gg_subseries(value) + facet_grid(variable ~ indcd + indname, scales = "free_y")
scatter_plot <- function(tbl_variable, date_field = "date", id_field = "stkcd", value_field = "value") { tbl_variable %>% dplyr::select(tidyselect::all_of( c(date_field, id_field, value_field) )) %>% tidyr::pivot_wider( names_from = tidyselect::all_of(id_field), values_from = tidyselect::all_of(value_field), id_cols = tidyselect::all_of(date_field) ) %>% dplyr::select(-tidyselect::all_of(c(date_field))) %>% GGally::ggpairs() } # Plot scatter plot for focused stocks nest_varible_focus_stock <- long_tsbl_focus_stock %>% dplyr::nest_by(.data$variable, .key = "tbl_variable") %>% dplyr::ungroup() nest_tsbl_focus_stock_scatter <- nest_varible_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tbl_variable, scatter_plot, date_field = "date", id_field = "stkcd", value_field = "value" ) ) nest_tsbl_focus_stock_scatter %>% dplyr::select(c("variable", "panel_plot")) %>% trelliscopejs::trelliscope( name = "scatter_plot_stocks", panel_col = "panel_plot", nrow = 1, ncol = 1, self_contained = TRUE, path = "trelliscope/scatter_plot/stocks" ) # Plot lag plot for focused industries nest_varible_focus_industry <- long_tsbl_focus_industry %>% dplyr::nest_by(.data$variable, .key = "tbl_variable") %>% dplyr::ungroup() nest_tsbl_focus_industry_scatter <- nest_varible_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tbl_variable, scatter_plot, date_field = "date", id_field = "indcd", value_field = "value" ) ) nest_tsbl_focus_industry_scatter %>% dplyr::select(c("variable", "panel_plot")) %>% trelliscopejs::trelliscope( name = "scatter_plot_industries", panel_col = "panel_plot", nrow = 1, ncol = 1, self_contained = TRUE, path = "trelliscope/scatter_plot/industries" )
lag_plot <- function(tsbl_variable, variable) { if (is.character(variable)) { variable <- rlang::parse_expr(variable) } tsbl_variable %>% feasts::gg_lag(y = {{ variable }}, geom = "point") } # Plot lag plot for focused stocks nest_tsbl_focus_stock_lag <- nest_tsbl_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, lag_plot, variable = "value" ) ) nest_tsbl_focus_stock_lag %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "lag_plot_stocks", panel_col = "panel_plot", nrow = 1, ncol = 1, self_contained = TRUE, path = "trelliscope/lag_plot/stocks" ) # Plot lag plot for focused industries nest_tsbl_focus_industry_lag <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, lag_plot, variable = "value" ) ) nest_tsbl_focus_industry_lag %>% dplyr::select(c("variable", "indcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "lag_plot_industries", panel_col = "panel_plot", nrow = 1, ncol = 1, self_contained = TRUE, path = "trelliscope/lag_plot/industries" )
acf_plot <- function(tsbl_variable, variable) { tsbl_variable %>% feasts::ACF(.data[[variable]]) %>% fabletools::autoplot() } # Plot ACF for focused stocks nest_tsbl_focus_stock_acf <- nest_tsbl_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, acf_plot, variable = "value" ) ) nest_tsbl_focus_stock_acf %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "acf_stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/autocorrelation/acf/stocks" ) # Plot ACF for focused industries nest_tsbl_focus_industry_acf <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, acf_plot, variable = "value" ) ) nest_tsbl_focus_industry_acf %>% dplyr::select(c("variable", "indcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "acf_industries", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/autocorrelation/acf/industries" )
pacf_plot <- function(tsbl_variable, variable) { tsbl_variable %>% feasts::PACF(.data[[variable]]) %>% fabletools::autoplot() } # Plot PACF for focused stocks nest_tsbl_focus_stock_pacf <- nest_tsbl_focus_stock %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, pacf_plot, variable = "value" ) ) nest_tsbl_focus_stock_pacf %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "pacf_stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/autocorrelation/pacf/stocks" ) # Plot PACF for focused industries nest_tsbl_focus_industry_pacf <- nest_tsbl_focus_industry %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, pacf_plot, variable = "value" ) ) nest_tsbl_focus_industry_pacf %>% dplyr::select(c("variable", "indcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "pacf_industries", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/autocorrelation/pacf/industries" )
decomp_plot <- function(decomp_model) { if (!fabletools::is_null_model(decomp_model[["model"]])) { decomp_model %>% fabletools::components() %>% fabletools::autoplot() } else { NULL } } # Plot decomp_plot for focused stocks nest_tsbl_focus_stock_stl <- nest_tsbl_focus_stock %>% dplyr::rowwise() %>% dplyr::mutate(decomp_model = list( fabletools::model(.data$tsbl_variable, model = feasts::STL(value)) )) %>% dplyr::ungroup() nest_tsbl_focus_stock_stl <- nest_tsbl_focus_stock_stl %>% dplyr::mutate( decomp_plot = trelliscopejs::map_plot( .data$decomp_model, decomp_plot ) ) nest_tsbl_focus_stock_stl %>% dplyr::select(variable, indcd, stkcd, decomp_plot) %>% trelliscopejs::trelliscope( name = "decomp_plot_of_stocks", panel_col = "decomp_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/decomp/stl/components/stocks" ) # Plot decomp_plot for focused industries nest_tsbl_focus_industry_stl <- nest_tsbl_focus_industry %>% dplyr::rowwise() %>% dplyr::mutate(decomp_model = list( fabletools::model(.data$tsbl_variable, model = feasts::STL(value)) )) %>% dplyr::ungroup() nest_tsbl_focus_industry_stl <- nest_tsbl_focus_industry_stl %>% dplyr::mutate( decomp_plot = trelliscopejs::map_plot( decomp_model, decomp_plot ) ) nest_tsbl_focus_industry_stl %>% dplyr::select(variable, indcd, decomp_plot) %>% trelliscopejs::trelliscope( name = "decomp_plot_industries", panel_col = "decomp_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/decomp/stl/components/industries" )
season_adjust_plot <- function(stl_model) { if (!fabletools::is_null_model(stl_model[["model"]])) { stl_model %>% fabletools::components() %>% ggplot(aes(x = .data$date)) + geom_line(aes(y = .data$value), color = "gray") + geom_line(aes(y = .data$season_adjust), color = "red") + theme(legend.position = "bottom") } else { NULL } } # Plot season_adjust_plot for focused stocks nest_tsbl_focus_stock_stl <- nest_tsbl_focus_stock_stl %>% dplyr::mutate( season_adjust_plot = trelliscopejs::map_plot( decomp_model, season_adjust_plot ) ) nest_tsbl_focus_stock_stl %>% dplyr::select(variable, indcd, stkcd, season_adjust_plot) %>% trelliscopejs::trelliscope( name = "season_adjust_plot_stock", panel_col = "season_adjust_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/decomp/stl/season_adjust/stocks" ) # Plot season_adjust_plot for focused industries nest_tsbl_focus_industry_stl <- nest_tsbl_focus_industry_stl %>% dplyr::mutate( season_adjust_plot = trelliscopejs::map_plot( decomp_model, season_adjust_plot ) ) nest_tsbl_focus_industry_stl %>% dplyr::select(variable, indcd, season_adjust_plot) %>% trelliscopejs::trelliscope( name = "season_adjust_plot_industries", panel_col = "season_adjust_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/decomp/stl/season_adjust/industries" )
simple_stats_plot <- function(tsbl_variable, value_fields = c("value")) { tsbl_variable %>% tidyr::pivot_longer( cols = tidyselect::all_of(value_fields), names_to = "name", values_to = "value" ) %>% ggplot(aes(x = name, y = value)) + geom_boxplot() + coord_flip() + labs(x = NULL, y = NULL) + theme_light() } # Simple stats of focused stocks # Compute simple stats of focused stocks simple_stats_focus_stock <- long_tsbl_focus_stock %>% dplyr::group_by(.data$stkcd, .data$variable) %>% fabletools::features( .var = .data$value, features = list( mean = ~ mean(., na.rm = TRUE), median = ~ median(., na.rm = TRUE), sd = ~ sd(., na.rm = TRUE), Q = ~ quantile(., na.rm = TRUE) ) ) %>% dplyr::arrange(.data$variable, .data$stkcd) knitr::kable(simple_stats_focus_stock, format = "html", digits = 3, caption = "Simple statstics of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Plot simple stats for focused stocks nest_tsbl_focus_simple_stats <- nest_tsbl_focus %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, simple_stats_plot, value_fields = c("value_stock", "value_industry") ) ) nest_tsbl_focus_simple_stats %>% dplyr::select(variable, stkcd, panel_plot) %>% trelliscopejs::trelliscope( name = "simple_stats of focused stocks", panel_col = "panel_plot", nrow = 2, ncol = 3, self_contained = TRUE, path = "trelliscope/simple_stats" ) # Simple stats of focused industries # Compute simple stats of focused industries simple_stats_focus_industry <- long_tsbl_focus_industry %>% dplyr::group_by(.data$indcd, .data$variable) %>% fabletools::features( .var = value, features = list( mean = ~ mean(., na.rm = TRUE), median = ~ median(., na.rm = TRUE), sd = ~ sd(., na.rm = TRUE), Q = ~ quantile(., na.rm = TRUE) ) ) %>% dplyr::arrange(.data$variable, .data$indcd) knitr::kable(simple_stats_focus_industry, format = "html", digits = 3, caption = "Simple statstics of focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped") # Plot simple stats of focused industries long_tsbl_focus_industry %>% ggplot(aes(x = indcd, y = value)) + geom_boxplot() + coord_flip() + facet_wrap(~variable, scales = "free")
# Compute features of ACF focused stocks feat_acf_focus_stock <- long_tsbl_focus_stock %>% dplyr::group_by(.data$stkcd, .data$variable) %>% fabletools::features( .var = .data$value, features = feasts::feat_acf, .period = 4 ) %>% dplyr::arrange(.data$variable, .data$stkcd) knitr::kable(feat_acf_focus_stock, format = "html", digits = 3, caption = "ACF features of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Compute features of ACF of focused industries feat_acf_focus_industry <- long_tsbl_focus_industry %>% dplyr::group_by(.data$indcd, .data$variable) %>% fabletools::features( .var = .data$value, features = feasts::feat_acf ) %>% dplyr::arrange(.data$variable, .data$indcd) knitr::kable(feat_acf_focus_industry, format = "html", digits = 3, caption = "STL features of focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped")
A time series decomposition can be used to measure the strength of trend and seasonality in a time series.
Recall that the decomposition is written as:
$$ y_t = T_t + S_{t} + R_t $$
$$F_T = \max\left(0, 1 - \frac{\text{Var}(R_t)}{\text{Var}(T_t+R_t)}\right)$$
For strongly trended data, the seasonally adjusted data should have much more variation than the remainder component. Therefore Var (R_t) /Var(T_t+R_t) should be relatively small. But for data with little or no trend, the two variances should be approximately the same.
$$F_T = \max\left(0, 1 - \frac{\text{Var}(R_t)}{\text{Var}(T_t+R_t)}\right)$$
A series with seasonal strength FS close to 0 exhibits almost no seasonality, while a series with strong seasonality will have FS close to 1 because Var(R_t) will be much smaller than Var(S_t+R_t)
# Compute features of an STL decomposition of focused stocks feat_stl_focus_stock <- long_tsbl_focus_stock %>% dplyr::group_by(.data$stkcd, .data$variable) %>% fabletools::features( .var = .data$value, features = feasts::feat_stl ) %>% dplyr::arrange(.data$variable, .data$stkcd) knitr::kable(feat_stl_focus_stock, format = "html", digits = 3, caption = "STL features of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") # Compute features of STL decomposition of focused industries feat_stl_focus_industry <- long_tsbl_focus_industry %>% dplyr::group_by(.data$indcd, .data$variable) %>% fabletools::features( .var = .data$value, features = feasts::feat_stl ) %>% dplyr::arrange(.data$variable, .data$indcd) knitr::kable(feat_stl_focus_industry, format = "html", digits = 3, caption = "STL features of focused industries" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute all features in feasts pkg for focused stocks feat_feasts_focus_stock <- long_tsbl_focus_stock %>% dplyr::group_by(.data$stkcd, .data$variable) %>% fabletools::features( .var = .data$value, features = fabletools::feature_set(pkgs = "feasts") ) %>% dplyr::arrange(.data$variable, .data$stkcd) # knitr::kable(feat_feasts_focus_stock, # format = "html", # digits = 3, # caption = "Feasts features of focused stocks" # ) %>% # kableExtra::kable_styling(latex_options = "striped") numeric_vars <- zstmodelr::expect_type_fields( feat_feasts_focus_stock, expect_type = "numeric" ) DT::datatable( feat_feasts_focus_stock, caption = "Feasts features of focused stocks", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) ) %>% DT::formatRound(columns = numeric_vars, digits = 3)
# Compute all features in feasts pkg for focused industries feat_feats_focus_industry <- long_tsbl_focus_industry %>% dplyr::group_by(.data$indcd, .data$variable) %>% fabletools::features( .var = .data$value, features = fabletools::feature_set(pkgs = "feasts") ) %>% dplyr::arrange(.data$variable, .data$indcd) # knitr::kable(feat_feats_focus_industry, # format = "html", # digits = 3, # caption = "Feasts features of focused stocks" # ) %>% # kableExtra::kable_styling(latex_options = "striped") numeric_vars <- zstmodelr::expect_type_fields( feat_feats_focus_industry, expect_type = "numeric" ) DT::datatable( feat_feats_focus_industry, caption = "Feasts features of focused industries", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) ) %>% DT::formatRound(columns = numeric_vars, digits = 3)
# Build train/test data dataset test_periods <- 4 date_field <- tsibble::index_var(long_tsbl_focus_stock) test_end_date <- max(dplyr::pull(long_tsbl_focus_stock[, date_field])) test_start_date <- test_end_date - test_periods + 1 train_end_date <- test_start_date - 1 long_tsbl_focus_stock_train <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(. ~ format(train_end_date)) long_tsbl_focus_stock_test <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(format(test_start_date) ~ .) # Forecast with simple methods long_tsbl_focus_stock_simple_fit <- long_tsbl_focus_stock_train %>% fabletools::model( Mean = fable::MEAN(value), Naive = fable::NAIVE(value), Seasonal_naive = fable::SNAIVE(value), Drift = fable::RW(value ~ drift()) ) long_tsbl_focus_stock_simple_forecast <- long_tsbl_focus_stock_simple_fit %>% fabletools::forecast(h = test_periods) long_tsbl_focus_stock_simple_forecast %>% autoplot(long_tsbl_focus_stock_train, level = NULL) + autolayer(long_tsbl_focus_stock_test, value, color = "black") + facet_grid(stkcd ~ variable, scale = "free")
long_tsbl_focus_stock_simple_fit %>% fabletools::augment()
# Plot diagnostics for residuals of fit models gg_tsresiduals_plot <- function(mdl_ts_fit) { # Turn mdl_ts(mdl_ts_fit) into mable(mdl_df) for gg_tsresidauls to plot tbl_model <- tibble::tibble(name = c("model_a"), fit_model = list(mdl_ts_fit)) mdl_model <- fabletools::as_mable(tbl_model, key = "name", model = "fit_model") p <- feasts::gg_tsresiduals(mdl_model) # Workaround solution: # Since gg_tsresiduals() can display properly in trelliscoejs, # I have to use following trics: # 1) gridExtra::arrangeGrob() to arrange sub-plots of residuals manually. # 2) Set chuck option fig.show = "hide" to prevent output in rmarkdown document p <- gridExtra::grid.arrange(p[[1]], p[[2]], p[[3]], layout_matrix = rbind(c(1, 1), (c(2, 3))) ) p } long_tsbl_focus_stock_simple_res_plot <- long_tsbl_focus_stock_simple_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(panel_plot = trelliscopejs::map_plot( fit_model, .f = gg_tsresiduals_plot )) long_tsbl_focus_stock_simple_res_plot %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "residuals_stocks", panel_col = "panel_plot", nrow = 1, ncol = 1, self_contained = TRUE, path = "trelliscope/forecast/simple/residuals" ) # Conduct Portmanteau test for residuals of fit models long_tsbl_focus_stock_simple_res_test <- long_tsbl_focus_stock_simple_fit %>% fabletools::augment() %>% fabletools::features(.resid, ljung_box, lag = 10, dof = 0) knitr::kable(long_tsbl_focus_stock_simple_res_test, format = "html", caption = "portmanteau test for simple forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
forecast_periods <- test_periods long_tsbl_focus_stock_simple_fit %>% fabletools::forecast(h = forecast_periods) %>% fabletools::hilo()
forecast_periods <- test_periods # Plot forecast of fit models forecast_plot <- function(fbl_forecast, tsbl_train) { fbl_forecast %>% fabletools::autoplot(tsbl_train) } long_tsbl_focus_stock_simple_forecast_benchmark <- long_tsbl_focus_stock_simple_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(forecast = purrr::map(fit_model, .f = ~ fabletools::forecast(.x, h = forecast_periods) )) %>% dplyr::left_join(nest_tsbl_focus_stock, by = c("stkcd", "indcd", "variable") ) long_tsbl_focus_stock_simple_forecast_benchmark <- long_tsbl_focus_stock_simple_forecast_benchmark %>% dplyr::mutate(panel_plot = trelliscopejs::map2_plot( .data$forecast, .data$tsbl_variable, .f = forecast_plot )) long_tsbl_focus_stock_simple_forecast_benchmark %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "Benchmark forecast", panel_col = "panel_plot", nrow = 2, ncol = 4, self_contained = TRUE, path = "trelliscope/forecast/simple/forecast_bechmark" )
forecast_periods <- test_periods # Plot forecast of fit models forecast_plot <- function(fbl_forecast, tsbl_train) { fbl_forecast %>% fabletools::autoplot(tsbl_train) } long_tsbl_focus_stock_simple_forecast_bootstrap <- long_tsbl_focus_stock_simple_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(forecast = purrr::map(fit_model, .f = ~ fabletools::forecast(.x, h = forecast_periods, bootstrap = TRUE, times = 100 ) )) %>% dplyr::left_join(nest_tsbl_focus_stock, by = c("stkcd", "indcd", "variable") ) long_tsbl_focus_stock_simple_forecast_bootstrap <- long_tsbl_focus_stock_simple_forecast_bootstrap %>% dplyr::mutate(panel_plot = trelliscopejs::map2_plot( .data$forecast, .data$tsbl_variable, .f = forecast_plot )) long_tsbl_focus_stock_simple_forecast_bootstrap %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "Bootstrap forecast", panel_col = "panel_plot", nrow = 2, ncol = 4, self_contained = TRUE, path = "trelliscope/forecast/simple/forecast_bootstrap" )
long_tsbl_focus_stock_simple_point_accuracy <- long_tsbl_focus_stock_simple_forecast %>% fabletools::accuracy(long_tsbl_focus_stock) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_simple_point_accuracy, format = "html", caption = "Point accuracy of simple forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped") long_tsbl_focus_stock_simple_point_accuracy_best <- long_tsbl_focus_stock_simple_point_accuracy %>% dplyr::group_by(variable, stkcd) %>% dplyr::slice_min(RMSE) knitr::kable(long_tsbl_focus_stock_simple_point_accuracy_best, format = "html", caption = "Best point accuracy of simple forecast of focused stocks(RMSE)" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute interval accuracy long_tsbl_focus_stock_simple_interval_accuracy <- long_tsbl_focus_stock_simple_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::interval_accuracy_measures ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_simple_interval_accuracy, format = "html", caption = "Interval accuracy of simple forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute distributional accuracy long_tsbl_focus_stock_simple_distribution_accuracy <- long_tsbl_focus_stock_simple_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::distribution_accuracy_measures ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_simple_distribution_accuracy, format = "html", caption = "distributional accuracy of simple forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
As with point forecasts, it is useful to be able to compare the distributional forecast accuracy of several methods across series on different scales. For point forecasts, we used scaled errors for that purpose. Another approach is to use skill scores. These can be used for both point forecast accuracy and distributional forecast accuracy.
# Compute skill scores for scale-free comparison long_tsbl_focus_stock_simple_forecast_skillscore_accuracy <- long_tsbl_focus_stock_simple_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = list(skill_rmse = fabletools::skill_score(RMSE)) ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_simple_forecast_skillscore_accuracy, format = "html", caption = "Skill scores for scale-free comparision of simple forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
stretch_long_tsbl_focus_stock_train <- long_tsbl_focus_stock_train %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) long_tsbl_focus_stock_simple_cv_fit <- stretch_long_tsbl_focus_stock_train %>% fabletools::model( Mean = fable::MEAN(value), Naive = fable::NAIVE(value), Seasonal_naive = fable::SNAIVE(value), Drift = fable::RW(value ~ drift()) ) long_tsbl_focus_stock_simple_cv_forecast <- long_tsbl_focus_stock_simple_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_simple_cv_point_accuracy <- long_tsbl_focus_stock_simple_cv_forecast %>% fabletools::accuracy(long_tsbl_focus_stock_train, measures = fabletools::point_accuracy_measures ) %>% dplyr::mutate(.type = "Cross-validation") %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_simple_cv_point_accuracy, format = "html", caption = "CV Point accuracy of simple forecast of focused stock" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Build train/test data dataset test_periods <- 4 date_field <- tsibble::index_var(long_tsbl_focus_stock) test_end_date <- max(dplyr::pull(long_tsbl_focus_stock[, date_field])) test_start_date <- test_end_date - test_periods + 1 train_end_date <- test_start_date - 1 long_tsbl_focus_stock_train <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(. ~ format(train_end_date)) long_tsbl_focus_stock_test <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(format(test_start_date) ~ .) nest_tsbl_focus_stock_train <- long_tsbl_focus_stock_train %>% tidyr::nest(tsbl_variable = c("date", "value")) %>% dplyr::ungroup() nest_tsbl_focus_stock_test <- long_tsbl_focus_stock_test %>% tidyr::nest(tsbl_variable = c("date", "value")) %>% dplyr::ungroup()
# Select ETS model automatically by AICs mdl_focus_stock_ets_candidate_model <- long_tsbl_focus_stock_train %>% fabletools::model( # Typical ETS models ETS_ses = fable::ETS(value ~ error("A") + trend("N") + season("N")), ETS_holt = fable::ETS(value ~ error("A") + trend("A") + season("N")), ETS_holt_damp = fable::ETS(value ~ error("A") + trend("Ad") + season("N")), ETS_holt_winters_a = fable::ETS(value ~ error("A") + trend("A") + season("A")), ETS_holt_winters_m = fable::ETS(value ~ error("A") + trend("A") + season("M")), ETS_holt_winters_damp_a = fable::ETS(value ~ error("A") + trend("Ad") + season("A")), ETS_holt_winters_damp_m = fable::ETS(value ~ error("A") + trend("Ad") + season("M")), # Model selected by AICs automatically ETS_Auto = fable::ETS(value, ic = "aicc") ) tbl_focus_stock_ets_candidate_model_spec <- mdl_focus_stock_ets_candidate_model %>% tidyr::pivot_longer( cols = tidyselect::contains("ETS"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate( spec = purrr::map(.data$fit_model, fabletools::model_sum) ) %>% tidyr::unnest(.data$spec) %>% dplyr::select(-c("fit_model")) tbl_focus_stock_ets_candidate_model_benchmark <- mdl_focus_stock_ets_candidate_model %>% fabletools::glance() tbl_focus_stock_ets_candidate_model <- tbl_focus_stock_ets_candidate_model_spec %>% dplyr::left_join(tbl_focus_stock_ets_candidate_model_benchmark, by = c("stkcd", "indcd", "variable", ".model") ) %>% dplyr::select(c("stkcd", "indcd", "variable", ".model", "spec", "AICc")) %>% dplyr::group_by(stkcd, indcd, variable) %>% dplyr::mutate(selected = (AICc == min(AICc))) %>% dplyr::arrange(AICc) tbl_focus_stock_ets_selected_model <- tbl_focus_stock_ets_candidate_model %>% dplyr::filter(.data$selected) %>% dplyr::select(-c("selected")) # Output selected results highlight_formatter <- formattable::formatter("span", style = ~ style( color = ifelse(selected, "red", "NA"), "font-weight" = ifelse(selected, "bold", NA) ) ) tbl_focus_stock_ets_candidate_model %>% formattable::formattable( list(~highlight_formatter, selected = FALSE ) ) %>% formattable::as.datatable( caption = "Candidate ETS models for focused stock", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) )
# Fit with ETS methods long_tsbl_focus_stock_ets_fit <- long_tsbl_focus_stock_train %>% fabletools::model( ETS_Auto = fable::ETS(value) )
tbl_focus_stock_ets_fit_model_spec <- long_tsbl_focus_stock_ets_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate(spec = purrr::map(fit_model, model_sum)) %>% dplyr::select(-c("fit_model")) %>% tidyr::unnest(.data$spec) tbl_focus_stock_ets_fit_model_spec %>% formattable::formattable()
fabletools::tidy(long_tsbl_focus_stock_ets_fit) %>% formattable::formattable(list(estimate = x ~ digits(x, digits = 2)))
fabletools::glance(long_tsbl_focus_stock_ets_fit) %>% formattable::formattable(list( formattable::area(col = sigma2:MAE) ~ purrr::partial( formattable::digits, digits = 2 ) ))
# Plot components of ETS model component_plot <- function(fit_model) { if (!fabletools::is_null_model(fit_model)) { fit_model %>% fabletools::components() %>% fabletools::autoplot() } else { NULL } } long_tsbl_focus_stock_ets_fit_component <- long_tsbl_focus_stock_ets_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( fit_model, .f = component_plot ) ) long_tsbl_focus_stock_ets_fit_component %>% dplyr::select(variable, indcd, stkcd, model, panel_plot) %>% trelliscopejs::trelliscope( name = "component_plot_of_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/ets/components/stocks" )
long_tsbl_focus_stock_ets_res_plot <- long_tsbl_focus_stock_ets_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(panel_plot = trelliscopejs::map_plot( fit_model, .f = gg_tsresiduals_plot )) long_tsbl_focus_stock_ets_res_plot %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "residuals_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/ets/residuals" ) # Conduct Portmanteau test for residuals of fit models long_tsbl_focus_stock_ets_res_test <- long_tsbl_focus_stock_ets_fit %>% fabletools::augment() %>% fabletools::features(.resid, ljung_box, lag = 10, dof = 0) knitr::kable(long_tsbl_focus_stock_ets_res_test, format = "html", caption = "portmanteau test for ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Forecast in test_periods long_tsbl_focus_stock_ets_forecast <- long_tsbl_focus_stock_ets_fit %>% fabletools::forecast(h = test_periods)
long_tsbl_focus_stock_ets_point_accuracy <- long_tsbl_focus_stock_ets_forecast %>% fabletools::accuracy(long_tsbl_focus_stock) %>% dplyr::left_join(tbl_focus_stock_ets_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_ets_point_accuracy, format = "html", caption = "Point accuracy of ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute interval accuracy long_tsbl_focus_stock_ets_interval_accuracy <- long_tsbl_focus_stock_ets_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::interval_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_ets_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_ets_interval_accuracy, format = "html", caption = "Interval accuracy of ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute distributional accuracy long_tsbl_focus_stock_ets_distribution_accuracy <- long_tsbl_focus_stock_ets_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::distribution_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_ets_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_ets_distribution_accuracy, format = "html", caption = "distributional accuracy of ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute skill scores for scale-free comparison long_tsbl_focus_stock_ets_forecast_skillscore_accuracy <- long_tsbl_focus_stock_ets_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = list(skill_rmse = fabletools::skill_score(RMSE)) ) %>% dplyr::left_join(tbl_focus_stock_ets_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_ets_forecast_skillscore_accuracy, format = "html", caption = "Skill scores for scale-free comparision of ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
stretch_long_tsbl_focus_stock <- long_tsbl_focus_stock %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) long_tsbl_focus_stock_ets_cv_fit <- stretch_long_tsbl_focus_stock %>% fabletools::model( ETS_Auto = fable::ETS(value) ) long_tsbl_focus_stock_ets_cv_forecast <- long_tsbl_focus_stock_ets_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_ets_cv_forecast <- long_tsbl_focus_stock_ets_cv_forecast %>% dplyr::mutate(interval = distributional::hilo(.data$value)) long_tsbl_focus_stock_ets_cv_forecast %>% ggplot(aes(x = date)) + geom_line(aes(y = .mean), color = "blue", linetype = "longdash") + geom_point(aes(y = .mean), color = "blue") + geom_line( data = stretch_long_tsbl_focus_stock, aes(y = value, group = .id), color = "black" ) + facet_grid(stkcd ~ variable, scale = "free") + gganimate::transition_reveal(as.Date(date)) # long_tsbl_focus_stock_ets_cv_forecast %>% # autoplot(stretch_long_tsbl_focus_stock, level = NULL, # show_gap = TRUE) + # autolayer(stretch_long_tsbl_focus_stock, value, color = "black") + # facet_grid(stkcd ~ variable, scale = "free")
# Safe version of difference tsibble difference_tsbl <- function(tsbl_variable, value_field = "value", result_field = "diff", season_diff = FALSE, differences = 1) { date_field <- tsibble::index(tsbl_variable) if (season_diff) { frequency <- tsibble::guess_frequency(tsbl_variable[[date_field]]) } else { frequency <- 1 } if (differences > 0) { tsbl_variable %>% dplyr::mutate({{ result_field }} := tsibble::difference(.data[[value_field]], lag = frequency, differences = differences )) } else { tsbl_variable %>% dplyr::mutate({{ result_field }} := .data[[value_field]]) } } # Firstly, try to conduct seasonal differencing tbl_focus_stock_train_ndiff <- long_tsbl_focus_stock_train %>% fabletools::features(.data$value, list(feasts::unitroot_nsdiffs)) nest_tsbl_focus_stock_train_diff <- nest_tsbl_focus_stock_train %>% dplyr::left_join(tbl_focus_stock_train_ndiff, by = c("stkcd", "indcd", "variable") ) %>% dplyr::rowwise() %>% dplyr::mutate( tsbl_variable = list( difference_tsbl(.data$tsbl_variable, value_field = "value", result_field = "sdiff", season_diff = TRUE, differences = .data$nsdiffs ) ) ) %>% dplyr::ungroup() # Secondly, try to conduct one-step differencing tbl_focus_stock_train_nsdiff <- long_tsbl_focus_stock_train %>% fabletools::features(.data$value, list(feasts::unitroot_ndiffs)) nest_tsbl_focus_stock_train_diff <- nest_tsbl_focus_stock_train_diff %>% dplyr::left_join(tbl_focus_stock_train_nsdiff, by = c("stkcd", "indcd", "variable") ) %>% dplyr::rowwise() %>% dplyr::mutate( tsbl_variable = list( difference_tsbl(.data$tsbl_variable, value_field = "sdiff", result_field = "diff", season_diff = FALSE, differences = .data$ndiffs ) ) ) %>% dplyr::ungroup() nest_tsbl_focus_stock_train_diff %>% dplyr::select(-c("tsbl_variable")) %>% knitr::kable( format = "html", caption = "Differences for stationary time sereis of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Plot partial plot for time series ts_partial_plot <- function(tsbl_variable, variable) { p <- tsbl_variable %>% feasts::gg_tsdisplay( y = .data[[variable]], plot_type = "partial" ) # Workaround solution: # Since gg_tsdidplay() can display properly in trelliscoejs, # I have to use following trics: # 1) gridExtra::arrangeGrob() to arrange sub-plots of residuals manually. # 2) Set chuck option fig.show = "hide" to prevent output in rmarkdown document p <- gridExtra::grid.arrange(p[[1]], p[[2]], p[[3]], layout_matrix = rbind(c(1, 1), (c(2, 3))) ) p } # Plot partial plot for focused stocks nest_tsbl_focus_stock_partial_plot <- nest_tsbl_focus_stock_train_diff %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( .data$tsbl_variable, ts_partial_plot, variable = "diff" ) ) nest_tsbl_focus_stock_partial_plot %>% dplyr::select(c("variable", "indcd", "stkcd", "panel_plot")) %>% trelliscopejs::trelliscope( name = "partial_plot_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/arima/partial" )
# Select ARIMA model automatically by AICs mdl_focus_stock_arima_candidate_model <- long_tsbl_focus_stock_train %>% fabletools::model( # Typical ARIMA models # Model selected by AICs automatically ARIMA_Auto = fable::ARIMA(value, ic = "aicc") ) tbl_focus_stock_arima_candidate_model_spec <- mdl_focus_stock_arima_candidate_model %>% tidyr::pivot_longer( cols = tidyselect::contains("ARIMA"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate( spec = purrr::map(.data$fit_model, fabletools::model_sum) ) %>% tidyr::unnest(.data$spec) %>% dplyr::select(-c("fit_model")) tbl_focus_stock_arima_candidate_model_benchmark <- mdl_focus_stock_arima_candidate_model %>% fabletools::glance() tbl_focus_stock_arima_candidate_model <- tbl_focus_stock_arima_candidate_model_spec %>% dplyr::left_join(tbl_focus_stock_arima_candidate_model_benchmark, by = c("stkcd", "indcd", "variable", ".model") ) %>% dplyr::select(c("stkcd", "indcd", "variable", ".model", "spec", "AICc")) %>% dplyr::group_by(stkcd, indcd, variable) %>% dplyr::mutate(selected = (AICc == min(AICc))) %>% dplyr::arrange(AICc) tbl_focus_stock_arima_selected_model <- tbl_focus_stock_arima_candidate_model %>% dplyr::filter(.data$selected) %>% dplyr::select(-c("selected")) # Output selected results highlight_formatter <- formattable::formatter("span", style = ~ style( color = ifelse(selected, "red", "NA"), "font-weight" = ifelse(selected, "bold", NA) ) ) tbl_focus_stock_arima_candidate_model %>% formattable::formattable( list(~highlight_formatter, selected = FALSE ) ) %>% formattable::as.datatable( caption = "Candidate ARIMA models for focused stock", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) )
# Fit with ARIMA methods long_tsbl_focus_stock_arima_fit <- long_tsbl_focus_stock_train %>% fabletools::model( ARIMA_Auto = fable::ARIMA(value) )
tbl_focus_stock_arima_fit_model_spec <- long_tsbl_focus_stock_arima_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate(spec = purrr::map(fit_model, model_sum)) %>% dplyr::select(-c("fit_model")) %>% tidyr::unnest(.data$spec) tbl_focus_stock_arima_fit_model_spec %>% formattable::formattable()
fabletools::tidy(long_tsbl_focus_stock_arima_fit) %>% formattable::formattable(list(estimate = x ~ digits(x, digits = 2)))
fabletools::glance(long_tsbl_focus_stock_arima_fit) %>% formattable::formattable(list( formattable::area(col = sigma2:BIC) ~ purrr::partial( formattable::digits, digits = 2 ) ))
long_tsbl_focus_stock_arima_res_plot <- long_tsbl_focus_stock_arima_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(panel_plot = trelliscopejs::map_plot( fit_model, .f = gg_tsresiduals_plot )) long_tsbl_focus_stock_arima_res_plot %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "residuals_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/arima/residuals" ) # Conduct Portmanteau test for residuals of fit models long_tsbl_focus_stock_arima_res_test <- long_tsbl_focus_stock_arima_fit %>% fabletools::augment() %>% fabletools::features(.resid, ljung_box, lag = 10, dof = 0) knitr::kable(long_tsbl_focus_stock_arima_res_test, format = "html", caption = "portmanteau test for arima forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Forecast in test_periods long_tsbl_focus_stock_arima_forecast <- long_tsbl_focus_stock_arima_fit %>% fabletools::forecast(h = test_periods)
long_tsbl_focus_stock_arima_point_accuracy <- long_tsbl_focus_stock_arima_forecast %>% fabletools::accuracy(long_tsbl_focus_stock) %>% dplyr::left_join(tbl_focus_stock_arima_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_arima_point_accuracy, format = "html", caption = "Point accuracy of arima forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute interval accuracy long_tsbl_focus_stock_arima_interval_accuracy <- long_tsbl_focus_stock_arima_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::interval_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_arima_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_arima_interval_accuracy, format = "html", caption = "Interval accuracy of arima forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute distributional accuracy long_tsbl_focus_stock_arima_distribution_accuracy <- long_tsbl_focus_stock_arima_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::distribution_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_arima_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_arima_distribution_accuracy, format = "html", caption = "distributional accuracy of arima forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute skill scores for scale-free comparison long_tsbl_focus_stock_arima_forecast_skillscore_accuracy <- long_tsbl_focus_stock_arima_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = list(skill_rmse = fabletools::skill_score(RMSE)) ) %>% dplyr::left_join(tbl_focus_stock_arima_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_arima_forecast_skillscore_accuracy, format = "html", caption = "Skill scores for scale-free comparision of ets forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
stretch_long_tsbl_focus_stock <- long_tsbl_focus_stock %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) long_tsbl_focus_stock_arima_cv_fit <- stretch_long_tsbl_focus_stock %>% fabletools::model( ARIMA_Auto = fable::ARIMA(value) ) long_tsbl_focus_stock_arima_cv_forecast <- long_tsbl_focus_stock_arima_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_arima_cv_forecast <- long_tsbl_focus_stock_arima_cv_forecast %>% dplyr::mutate(interval = distributional::hilo(.data$value)) long_tsbl_focus_stock_arima_cv_forecast %>% ggplot(aes(x = date)) + geom_line(aes(y = .mean), color = "blue", linetype = "longdash") + geom_point(aes(y = .mean), color = "blue") + geom_line( data = stretch_long_tsbl_focus_stock, aes(y = value, group = .id), color = "black" ) + facet_grid(stkcd ~ variable, scale = "free") + gganimate::transition_reveal(as.Date(date))
# Fit with prophet model long_tsbl_focus_stock_prophet_fit <- long_tsbl_focus_stock_train %>% fabletools::model( Prophet_Auto = fable.prophet::prophet(value) )
tbl_focus_stock_prophet_fit_model_spec <- long_tsbl_focus_stock_prophet_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate(spec = purrr::map(fit_model, model_sum)) %>% dplyr::select(-c("fit_model")) %>% tidyr::unnest(.data$spec) tbl_focus_stock_prophet_fit_model_spec %>% formattable::formattable()
fabletools::tidy(long_tsbl_focus_stock_prophet_fit) %>% formattable::formattable(list(estimate = x ~ digits(x, digits = 2)))
fabletools::glance(long_tsbl_focus_stock_prophet_fit) %>% formattable::formattable(list( formattable::area(col = sigma) ~ purrr::partial( formattable::digits, digits = 2 ) ))
# Plot components of prophet model component_plot <- function(fit_model) { if (!fabletools::is_null_model(fit_model)) { fit_model %>% fabletools::components() %>% fabletools::autoplot() } else { NULL } } long_tsbl_focus_stock_prophet_fit_component <- long_tsbl_focus_stock_prophet_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate( panel_plot = trelliscopejs::map_plot( fit_model, .f = component_plot ) ) long_tsbl_focus_stock_prophet_fit_component %>% dplyr::select(variable, indcd, stkcd, model, panel_plot) %>% trelliscopejs::trelliscope( name = "component_plot_of_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/prophet/components/stocks" )
long_tsbl_focus_stock_prophet_res_plot <- long_tsbl_focus_stock_prophet_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(panel_plot = trelliscopejs::map_plot( fit_model, .f = gg_tsresiduals_plot )) long_tsbl_focus_stock_prophet_res_plot %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "residuals_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/prophet/residuals" ) # Conduct Portmanteau test for residuals of fit models long_tsbl_focus_stock_prophet_res_test <- long_tsbl_focus_stock_prophet_fit %>% fabletools::augment() %>% fabletools::features(.resid, ljung_box, lag = 10, dof = 0) knitr::kable(long_tsbl_focus_stock_prophet_res_test, format = "html", caption = "portmanteau test for prophet forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Forecast in test_periods long_tsbl_focus_stock_prophet_forecast <- long_tsbl_focus_stock_prophet_fit %>% fabletools::forecast(h = test_periods)
long_tsbl_focus_stock_prophet_point_accuracy <- long_tsbl_focus_stock_prophet_forecast %>% fabletools::accuracy(long_tsbl_focus_stock) %>% dplyr::left_join(tbl_focus_stock_prophet_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_prophet_point_accuracy, format = "html", caption = "Point accuracy of prophet forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute interval accuracy long_tsbl_focus_stock_prophet_interval_accuracy <- long_tsbl_focus_stock_prophet_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::interval_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_prophet_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_prophet_interval_accuracy, format = "html", caption = "Interval accuracy of arima forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute distributional accuracy long_tsbl_focus_stock_prophet_distribution_accuracy <- long_tsbl_focus_stock_prophet_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::distribution_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_prophet_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_prophet_distribution_accuracy, format = "html", caption = "distributional accuracy of prophet forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute skill scores for scale-free comparison long_tsbl_focus_stock_prophet_forecast_skillscore_accuracy <- long_tsbl_focus_stock_prophet_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = list(skill_rmse = fabletools::skill_score(RMSE)) ) %>% dplyr::left_join(tbl_focus_stock_prophet_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_prophet_forecast_skillscore_accuracy, format = "html", caption = "Skill scores for scale-free comparision of prophet forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
stretch_long_tsbl_focus_stock <- long_tsbl_focus_stock %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) long_tsbl_focus_stock_prophet_cv_fit <- stretch_long_tsbl_focus_stock %>% fabletools::model( Prophet_Auto = fable.prophet::prophet(value) ) long_tsbl_focus_stock_prophet_cv_forecast <- long_tsbl_focus_stock_prophet_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_prophet_cv_forecast <- long_tsbl_focus_stock_prophet_cv_forecast %>% dplyr::mutate(interval = distributional::hilo(.data$value)) long_tsbl_focus_stock_prophet_cv_forecast %>% ggplot(aes(x = date)) + geom_line(aes(y = .mean), color = "blue", linetype = "longdash") + geom_point(aes(y = .mean), color = "blue") + geom_line( data = stretch_long_tsbl_focus_stock, aes(y = value, group = .id), color = "black" ) + facet_grid(stkcd ~ variable, scale = "free") + gganimate::transition_reveal(as.Date(date))
# Fit with NNETAR model long_tsbl_focus_stock_nnetar_fit <- long_tsbl_focus_stock_train %>% fabletools::model( NNETAR_Auto = fable::NNETAR(value) )
tbl_focus_stock_nnetar_fit_model_spec <- long_tsbl_focus_stock_nnetar_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = ".model", values_to = "fit_model" ) %>% dplyr::mutate(spec = purrr::map(fit_model, model_sum)) %>% dplyr::select(-c("fit_model")) %>% tidyr::unnest(.data$spec) tbl_focus_stock_nnetar_fit_model_spec %>% formattable::formattable()
fabletools::tidy(long_tsbl_focus_stock_nnetar_fit) %>% formattable::formattable(list(estimate = x ~ digits(x, digits = 2)))
fabletools::glance(long_tsbl_focus_stock_nnetar_fit) %>% formattable::formattable(list( formattable::area(col = sigma2) ~ purrr::partial( formattable::digits, digits = 2 ) ))
long_tsbl_focus_stock_nnetar_res_plot <- long_tsbl_focus_stock_nnetar_fit %>% tidyr::pivot_longer(-c("stkcd", "indcd", "variable"), names_to = "model", values_to = "fit_model" ) %>% dplyr::mutate(panel_plot = trelliscopejs::map_plot( fit_model, .f = gg_tsresiduals_plot )) long_tsbl_focus_stock_nnetar_res_plot %>% tibble::as_tibble() %>% dplyr::select(c("variable", "indcd", "stkcd", "model", "panel_plot")) %>% trelliscopejs::trelliscope( name = "residuals_stocks", panel_col = "panel_plot", nrow = 1, ncol = 2, self_contained = TRUE, path = "trelliscope/forecast/nnetar/residuals" ) # Conduct Portmanteau test for residuals of fit models long_tsbl_focus_stock_nnetar_res_test <- long_tsbl_focus_stock_nnetar_fit %>% fabletools::augment() %>% fabletools::features(.resid, ljung_box, lag = 10, dof = 0) knitr::kable(long_tsbl_focus_stock_nnetar_res_test, format = "html", caption = "portmanteau test for nnetar forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Forecast in test_periods long_tsbl_focus_stock_nnetar_forecast <- long_tsbl_focus_stock_nnetar_fit %>% fabletools::forecast(h = test_periods)
long_tsbl_focus_stock_nnetar_point_accuracy <- long_tsbl_focus_stock_nnetar_forecast %>% fabletools::accuracy(long_tsbl_focus_stock) %>% dplyr::left_join(tbl_focus_stock_nnetar_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_nnetar_point_accuracy, format = "html", caption = "Point accuracy of nnetar forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute interval accuracy long_tsbl_focus_stock_nnetar_interval_accuracy <- long_tsbl_focus_stock_nnetar_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::interval_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_nnetar_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_nnetar_interval_accuracy, format = "html", caption = "Interval accuracy of nnetar forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute distributional accuracy long_tsbl_focus_stock_nnetar_distribution_accuracy <- long_tsbl_focus_stock_nnetar_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::distribution_accuracy_measures ) %>% dplyr::left_join(tbl_focus_stock_nnetar_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_nnetar_distribution_accuracy, format = "html", caption = "distributional accuracy of nnetar forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
# Compute skill scores for scale-free comparison long_tsbl_focus_stock_nnetar_forecast_skillscore_accuracy <- long_tsbl_focus_stock_nnetar_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = list(skill_rmse = fabletools::skill_score(RMSE)) ) %>% dplyr::left_join(tbl_focus_stock_nnetar_fit_model_spec, by = c(".model", "stkcd", "indcd", "variable") ) %>% dplyr::select( stkcd, indcd, variable, .model, spec, .type, tidyselect::everything() ) %>% dplyr::arrange(variable, stkcd, .model) knitr::kable(long_tsbl_focus_stock_nnetar_forecast_skillscore_accuracy, format = "html", caption = "Skill scores for scale-free comparision of nnetar forecast of focused stocks" ) %>% kableExtra::kable_styling(latex_options = "striped")
stretch_long_tsbl_focus_stock <- long_tsbl_focus_stock %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) long_tsbl_focus_stock_nnetar_cv_fit <- stretch_long_tsbl_focus_stock %>% fabletools::model( NNETAR_Auto = fable::NNETAR(value) ) long_tsbl_focus_stock_nnetar_cv_forecast <- long_tsbl_focus_stock_nnetar_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_nnetar_cv_forecast <- long_tsbl_focus_stock_nnetar_cv_forecast %>% dplyr::mutate(interval = distributional::hilo(.data$value)) long_tsbl_focus_stock_nnetar_cv_forecast %>% ggplot(aes(x = date)) + geom_line(aes(y = .mean), color = "blue", linetype = "longdash") + geom_point(aes(y = .mean), color = "blue") + geom_line( data = stretch_long_tsbl_focus_stock, aes(y = value, group = .id), color = "black" ) + facet_grid(stkcd ~ variable, scale = "free") + gganimate::transition_reveal(as.Date(date))
# Build train/test data dataset test_periods <- 4 date_field <- tsibble::index_var(long_tsbl_focus_stock) test_end_date <- max(dplyr::pull(long_tsbl_focus_stock[, date_field])) test_start_date <- test_end_date - test_periods + 1 train_end_date <- test_start_date - 1 long_tsbl_focus_stock_train <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(. ~ format(train_end_date)) long_tsbl_focus_stock_test <- long_tsbl_focus_stock %>% tsibble::group_by_key() %>% tsibble::filter_index(format(test_start_date) ~ .) stretch_long_tsbl_focus_stock_train <- long_tsbl_focus_stock_train %>% tsibble::stretch_tsibble(.init = 4, .step = 1) %>% dplyr::filter(.data$.id != max(.data$.id)) test_models <- list( # Simple models as baseline Mean = fable::MEAN(value), Naive = fable::NAIVE(value), Seasonal_naive = fable::SNAIVE(value), Drift = fable::RW(value ~ drift()), # Advanced models to compare ETS_Auto = fable::ETS(value, ic = "aicc"), ARIMA_Auto = fable::ARIMA(value, ic = "aicc"), Prophet_Auto = fable.prophet::prophet(value), NNETAR_Auto = fable::NNETAR(value) )
future::plan("multicore") long_tsbl_focus_stock_model_cv_fit <- stretch_long_tsbl_focus_stock_train %>% fabletools::model( !!!test_models ) long_tsbl_focus_stock_model_cv_forecast <- long_tsbl_focus_stock_model_cv_fit %>% fabletools::forecast(h = 1) long_tsbl_focus_stock_model_cv_point_accuracy <- long_tsbl_focus_stock_model_cv_forecast %>% fabletools::accuracy(long_tsbl_focus_stock_train, measures = fabletools::point_accuracy_measures ) %>% dplyr::mutate(.type = "Cross-validation") %>% dplyr::group_by(.data$stkcd, .data$indcd, .data$variable) %>% dplyr::mutate(rank = dplyr::row_number(.data$RMSE)) %>% dplyr::select(c("stkcd", "indcd", "variable"), tidyselect::everything()) %>% dplyr::arrange(.data$variable, .data$stkcd, .data$rank) # Output assessment results highlight_formatter <- formattable::formatter("span", style = ~ style( color = ifelse(rank == 1, "red", "NA"), "font-weight" = ifelse(rank == 1, "bold", NA) ) ) long_tsbl_focus_stock_model_cv_point_accuracy %>% formattable::formattable( list( ~highlight_formatter, RMSE = formattable::color_tile("lightblue", "white") ) ) %>% formattable::as.datatable( caption = "Assessing models for focused stocks by cv", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) )
long_tsbl_focus_stock_model_fit <- long_tsbl_focus_stock_train %>% fabletools::model( !!!test_models ) long_tsbl_focus_stock_model_forecast <- long_tsbl_focus_stock_model_fit %>% fabletools::forecast(h = test_periods) long_tsbl_focus_stock_model_point_accuracy <- long_tsbl_focus_stock_model_forecast %>% fabletools::accuracy(long_tsbl_focus_stock, measures = fabletools::point_accuracy_measures ) %>% dplyr::group_by(.data$stkcd, .data$indcd, .data$variable) %>% dplyr::mutate(rank = dplyr::row_number(.data$RMSE)) %>% dplyr::select(c("stkcd", "indcd", "variable"), tidyselect::everything()) %>% dplyr::arrange(.data$variable, .data$stkcd, .data$rank) # Output benchmark results highlight_formatter <- formattable::formatter("span", style = ~ style( color = ifelse(rank == 1, "red", "NA"), "font-weight" = ifelse(rank == 1, "bold", NA) ) ) long_tsbl_focus_stock_model_point_accuracy %>% formattable::formattable( list( ~highlight_formatter, RMSE = formattable::color_tile("lightblue", "white") ) ) %>% formattable::as.datatable( caption = "Assessing ETS models for focused stock by testing data", filter = "top", extensions = "Scroller", rownames = FALSE, options = list( columnDefs = list( list(className = "dt-left", targets = "_all") ), pageLength = 5, dom = "ltir", deferRender = TRUE, scrollY = 180, scrollX = TRUE, scroller = TRUE ) )
# Restore future plan if (!is.null(old_future_plan)) { future::future(old_future_plan) }
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.