├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check-full.yaml │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── recheck.yml ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── acf.R ├── attach.R ├── classical.R ├── compact-purrr.R ├── dcmp_checks.R ├── feasts.R ├── features.R ├── gg_geom.R ├── graphics.R ├── guerrero.R ├── reexports.R ├── seats.R ├── specials.R ├── stl.R ├── tests.R ├── utils.R ├── x11.R ├── x13arimaseats.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── feasts.Rproj ├── hex ├── feasts.ai ├── feasts.png ├── feasts.svg └── feasts_icon.png ├── man ├── ACF.Rd ├── STL.Rd ├── X_13ARIMA_SEATS.Rd ├── autoplot.tbl_cf.Rd ├── classical_decomposition.Rd ├── coef_hurst.Rd ├── cointegration_johansen.Rd ├── cointegration_phillips_ouliaris.Rd ├── feasts-package.Rd ├── feat_acf.Rd ├── feat_intermittent.Rd ├── feat_pacf.Rd ├── feat_spectral.Rd ├── feat_stl.Rd ├── figures │ ├── README-acf-1.png │ ├── README-dcmp-1.png │ ├── README-dcmp-plot-1.png │ ├── README-extreme-1.png │ ├── README-features-plot-1.png │ ├── README-graphics-1.png │ ├── README-graphics-2.png │ ├── README-graphics-3.png │ ├── README-graphics-4.png │ ├── README-unnamed-chunk-1-1.png │ ├── README-unnamed-chunk-2-1.png │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-soft-deprecated.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── logo.png ├── generate.stl_decomposition.Rd ├── gg_arma.Rd ├── gg_irf.Rd ├── gg_lag.Rd ├── gg_season.Rd ├── gg_subseries.Rd ├── gg_tsdisplay.Rd ├── gg_tsresiduals.Rd ├── guerrero.Rd ├── longest_flat_spot.Rd ├── n_crossing_points.Rd ├── portmanteau_tests.Rd ├── reexports.Rd ├── scale_cf_lag.Rd ├── shift_level_max.Rd ├── stat_arch_lm.Rd ├── tile_features.Rd ├── unitroot.Rd └── unitroot_ndiffs.Rd ├── tests ├── testthat.R └── testthat │ ├── test-cf.R │ ├── test-classical.R │ ├── test-features.R │ ├── test-graphics.R │ ├── test-seats.R │ ├── test-stl.R │ └── test-x11.R └── vignettes ├── .gitignore └── feasts.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^docs$ 2 | ^_pkgdown\.yml$ 3 | ^feasts\.Rproj$ 4 | ^\.Rproj\.user$ 5 | ^README\.Rmd$ 6 | ^appveyor\.yml$ 7 | ^\.travis\.yml$ 8 | ^tic\.R$ 9 | ^codecov\.yml$ 10 | ^hex$ 11 | ^cran-comments\.md$ 12 | ^.github$ 13 | ^revdep$ 14 | ^pkgdown$ 15 | ^CRAN-RELEASE$ 16 | ^\.github$ 17 | ^CRAN-SUBMISSION$ 18 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check-full.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | workflow_dispatch: 9 | 10 | name: R-CMD-check-full.yaml 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | R-CMD-check: 16 | runs-on: ${{ matrix.config.os }} 17 | 18 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | config: 24 | - {os: macos-latest, r: 'release'} 25 | 26 | - {os: windows-latest, r: 'release'} 27 | # use 4.0 or 4.1 to check with rtools40's older compiler 28 | - {os: windows-latest, r: 'oldrel-4'} 29 | 30 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 31 | - {os: ubuntu-latest, r: 'release'} 32 | - {os: ubuntu-latest, r: 'oldrel-1'} 33 | - {os: ubuntu-latest, r: 'oldrel-2'} 34 | - {os: ubuntu-latest, r: 'oldrel-3'} 35 | - {os: ubuntu-latest, r: 'oldrel-4'} 36 | 37 | env: 38 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 39 | R_KEEP_PKG_SOURCE: yes 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | 44 | - uses: r-lib/actions/setup-pandoc@v2 45 | 46 | - uses: r-lib/actions/setup-r@v2 47 | with: 48 | r-version: ${{ matrix.config.r }} 49 | http-user-agent: ${{ matrix.config.http-user-agent }} 50 | use-public-rspm: true 51 | 52 | - uses: r-lib/actions/setup-r-dependencies@v2 53 | with: 54 | extra-packages: any::rcmdcheck 55 | needs: check 56 | 57 | - uses: r-lib/actions/check-r-package@v2 58 | with: 59 | upload-snapshots: true 60 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 61 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | R-CMD-check: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | R_KEEP_PKG_SOURCE: yes 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::rcmdcheck 29 | needs: check 30 | 31 | - uses: r-lib/actions/check-r-package@v2 32 | with: 33 | upload-snapshots: true 34 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 35 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | # Only deploy docs when master is changed 6 | branches: [main, master] 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown 12 | 13 | jobs: 14 | pkgdown: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/setup-pandoc@v2 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | # see https://github.com/r-lib/actions/tree/v2/setup-r-dependencies#installing-the-local-package 30 | extra-packages: pkgdown, local::. 31 | needs: website 32 | 33 | - name: Deploy package 34 | run: | 35 | git config --local user.name "$GITHUB_ACTOR" 36 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 37 | Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 38 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | issue_comment: 3 | types: [created] 4 | name: Commands 5 | jobs: 6 | document: 7 | if: startsWith(github.event.comment.body, '/document') 8 | name: document 9 | runs-on: macOS-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - uses: r-lib/actions/pr-fetch@master 13 | with: 14 | repo-token: ${{ secrets.GH_PAT }} 15 | - uses: r-lib/actions/setup-r@master 16 | - name: Install dependencies 17 | run: Rscript -e 'install.packages(c("remotes", "roxygen2"))' -e 'remotes::install_deps(dependencies = TRUE)' 18 | - name: Document 19 | run: Rscript -e 'roxygen2::roxygenise()' 20 | - name: commit 21 | run: | 22 | git add man/\* NAMESPACE 23 | git commit -m 'Document' 24 | - uses: r-lib/actions/pr-push@master 25 | with: 26 | repo-token: ${{ secrets.GH_PAT }} 27 | style: 28 | if: startsWith(github.event.comment.body, '/style') 29 | name: document 30 | runs-on: macOS-latest 31 | steps: 32 | - uses: actions/checkout@master 33 | - uses: r-lib/actions/pr-fetch@master 34 | with: 35 | repo-token: ${{ secrets.GH_PAT }} 36 | - uses: r-lib/actions/setup-r@master 37 | - name: Install dependencies 38 | run: Rscript -e 'install.packages("styler")' 39 | - name: style 40 | run: Rscript -e 'styler::style_pkg()' 41 | - name: commit 42 | run: | 43 | git add \*.R 44 | git commit -m 'style' 45 | - uses: r-lib/actions/pr-push@master 46 | with: 47 | repo-token: ${{ secrets.GH_PAT }} 48 | -------------------------------------------------------------------------------- /.github/workflows/recheck.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | which: 5 | type: choice 6 | description: Which dependents to check 7 | options: 8 | - strong 9 | - most 10 | 11 | name: Reverse dependency check 12 | 13 | jobs: 14 | revdep_check: 15 | name: Reverse check ${{ inputs.which }} dependents 16 | uses: r-devel/recheck/.github/workflows/recheck.yml@v1 17 | with: 18 | which: ${{ inputs.which }} 19 | subdirectory: '' #if your package is in a git subdir -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | docs/ 5 | inst/doc 6 | tests/testthat/Rplots.pdf 7 | revdep 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: feasts 2 | Title: Feature Extraction and Statistics for Time Series 3 | Version: 0.4.1.9000 4 | Description: Provides a collection of features, decomposition methods, 5 | statistical summaries and graphics functions for the analysing tidy time 6 | series data. The package name 'feasts' is an acronym comprising of its key 7 | features: Feature Extraction And Statistics for Time Series. 8 | Authors@R: c( 9 | person("Mitchell", "O'Hara-Wild", email = "mail@mitchelloharawild.com", role = c("aut", "cre")), 10 | person("Rob", "Hyndman", role = "aut"), 11 | person("Earo", "Wang", role = "aut"), 12 | person("Di", "Cook", role = "ctb"), 13 | person("Thiyanga", "Talagala", role = "ctb", comment = "Correlation features"), 14 | person("Leanne", "Chhay", role = "ctb", comment = "Guerrero's method") 15 | ) 16 | Depends: 17 | R (>= 3.5.0), 18 | fabletools (>= 0.3.1) 19 | Imports: 20 | rlang (>= 0.2.0), 21 | tibble (>= 1.4.1), 22 | tsibble (>= 0.9.0), 23 | ggplot2 (>= 3.0.0), 24 | dplyr (>= 1.0.0), 25 | tidyr (>= 0.8.3), 26 | scales (>= 1.1.0), 27 | vctrs, 28 | lubridate, 29 | grid, 30 | slider, 31 | utils, 32 | lifecycle, 33 | gtable 34 | Suggests: 35 | tsibbledata, 36 | pillar (>= 1.0.1), 37 | knitr, 38 | rmarkdown, 39 | testthat, 40 | covr, 41 | seasonal, 42 | urca, 43 | fracdiff, 44 | fable, 45 | ggrepel 46 | ByteCompile: true 47 | VignetteBuilder: knitr 48 | License: GPL-3 49 | URL: http://feasts.tidyverts.org/, https://github.com/tidyverts/feasts/ 50 | BugReports: https://github.com/tidyverts/feasts/issues 51 | Encoding: UTF-8 52 | RoxygenNote: 7.3.2.9000 53 | Roxygen: list(markdown = TRUE, roclets=c('rd', 'collate', 'namespace')) 54 | Language: en-GB 55 | RdMacros: lifecycle 56 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method("+",gg_tsensemble) 4 | S3method(as_lag,default) 5 | S3method(as_lag,interval) 6 | S3method(autoplot,tbl_cf) 7 | S3method(components,classical_decomposition) 8 | S3method(components,feasts_x13arimaseats) 9 | S3method(components,stl_decomposition) 10 | S3method(components,x11_decomposition) 11 | S3method(components,x13_decomposition) 12 | S3method(fitted,stl_decomposition) 13 | S3method(floor_tsibble_date,default) 14 | S3method(floor_tsibble_date,numeric) 15 | S3method(floor_tsibble_date,yearmonth) 16 | S3method(floor_tsibble_date,yearquarter) 17 | S3method(floor_tsibble_date,yearweek) 18 | S3method(format,cf_lag) 19 | S3method(generate,stl_decomposition) 20 | S3method(grid.draw,gg_tsensemble) 21 | S3method(index_valid,cf_lag) 22 | S3method(interval_pull,cf_lag) 23 | S3method(model_sum,classical_decomposition) 24 | S3method(model_sum,feasts_x13arimaseats) 25 | S3method(model_sum,stl_decomposition) 26 | S3method(outliers,feasts_x13arimaseats) 27 | S3method(print,gg_tsensemble) 28 | S3method(report,feasts_x13arimaseats) 29 | S3method(residuals,stl_decomposition) 30 | S3method(scale_type,cf_lag) 31 | S3method(vec_arith,cf_lag) 32 | S3method(vec_cast,cf_lag) 33 | S3method(vec_cast.cf_lag,default) 34 | S3method(vec_cast.cf_lag,double) 35 | S3method(vec_cast.character,cf_lag) 36 | S3method(vec_cast.double,cf_lag) 37 | S3method(vec_ptype2,cf_lag) 38 | S3method(vec_ptype2.cf_lag,default) 39 | S3method(vec_ptype2.cf_lag,double) 40 | S3method(vec_ptype2.double,cf_lag) 41 | S3method(vec_ptype_full,cf_lag) 42 | export("%>%") 43 | export(ACF) 44 | export(CCF) 45 | export(PACF) 46 | export(STL) 47 | export(X_13ARIMA_SEATS) 48 | export(as_tsibble) 49 | export(autolayer) 50 | export(autoplot) 51 | export(box_pierce) 52 | export(classical_decomposition) 53 | export(coef_hurst) 54 | export(cointegration_johansen) 55 | export(cointegration_phillips_ouliaris) 56 | export(feat_acf) 57 | export(feat_intermittent) 58 | export(feat_pacf) 59 | export(feat_spectral) 60 | export(feat_stl) 61 | export(gg_arma) 62 | export(gg_irf) 63 | export(gg_lag) 64 | export(gg_season) 65 | export(gg_subseries) 66 | export(gg_tsdisplay) 67 | export(gg_tsresiduals) 68 | export(guerrero) 69 | export(ljung_box) 70 | export(longest_flat_spot) 71 | export(n_crossing_points) 72 | export(n_flat_spots) 73 | export(portmanteau_tests) 74 | export(scale_x_cf_lag) 75 | export(shift_kl_max) 76 | export(shift_level_max) 77 | export(shift_var_max) 78 | export(stat_arch_lm) 79 | export(unitroot_kpss) 80 | export(unitroot_ndiffs) 81 | export(unitroot_nsdiffs) 82 | export(unitroot_pp) 83 | export(var_tiled_mean) 84 | export(var_tiled_var) 85 | import(rlang) 86 | import(tsibble) 87 | import(vctrs) 88 | importFrom(dplyr,"%>%") 89 | importFrom(dplyr,distinct) 90 | importFrom(dplyr,filter) 91 | importFrom(dplyr,group_by) 92 | importFrom(dplyr,lead) 93 | importFrom(dplyr,mutate) 94 | importFrom(dplyr,summarise) 95 | importFrom(dplyr,transmute) 96 | importFrom(dplyr,ungroup) 97 | importFrom(fabletools,as_dable) 98 | importFrom(fabletools,components) 99 | importFrom(fabletools,generate) 100 | importFrom(fabletools,get_frequencies) 101 | importFrom(fabletools,model_sum) 102 | importFrom(fabletools,new_model_class) 103 | importFrom(fabletools,new_model_definition) 104 | importFrom(fabletools,outliers) 105 | importFrom(fabletools,report) 106 | importFrom(ggplot2,aes) 107 | importFrom(ggplot2,autolayer) 108 | importFrom(ggplot2,autoplot) 109 | importFrom(ggplot2,facet_grid) 110 | importFrom(ggplot2,facet_wrap) 111 | importFrom(ggplot2,geom_abline) 112 | importFrom(ggplot2,geom_histogram) 113 | importFrom(ggplot2,geom_hline) 114 | importFrom(ggplot2,geom_line) 115 | importFrom(ggplot2,geom_path) 116 | importFrom(ggplot2,geom_point) 117 | importFrom(ggplot2,ggplot) 118 | importFrom(ggplot2,ggtitle) 119 | importFrom(ggplot2,layer) 120 | importFrom(ggplot2,scale_type) 121 | importFrom(ggplot2,vars) 122 | importFrom(ggplot2,xlab) 123 | importFrom(ggplot2,ylab) 124 | importFrom(ggplot2,ylim) 125 | importFrom(grid,grid.draw) 126 | importFrom(lubridate,as_date) 127 | importFrom(lubridate,month) 128 | importFrom(lubridate,year) 129 | importFrom(lubridate,years) 130 | importFrom(stats,as.ts) 131 | importFrom(stats,coef) 132 | importFrom(stats,complete.cases) 133 | importFrom(stats,decompose) 134 | importFrom(stats,embed) 135 | importFrom(stats,frequency) 136 | importFrom(stats,lm) 137 | importFrom(stats,median) 138 | importFrom(stats,model.frame) 139 | importFrom(stats,na.contiguous) 140 | importFrom(stats,na.exclude) 141 | importFrom(stats,optimise) 142 | importFrom(stats,qnorm) 143 | importFrom(stats,residuals) 144 | importFrom(stats,sd) 145 | importFrom(stats,stl) 146 | importFrom(stats,ts) 147 | importFrom(stats,var) 148 | importFrom(tibble,tibble) 149 | importFrom(tidyr,gather) 150 | importFrom(tsibble,as_tsibble) 151 | importFrom(utils,tail) 152 | importFrom(vctrs,vec_cast) 153 | importFrom(vctrs,vec_ptype2) 154 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # feasts (development version) 2 | 3 | ## New features 4 | 5 | * `gg_tsresiduals()` now supports the `plot_type` argument to customise the 6 | third plot, much like `gg_tsdisplay()`. 7 | 8 | # feasts 0.4.1 9 | 10 | ## Bug fixes 11 | 12 | * Fixed `gg_season()` not working with daily data showing seasonality > 1 week. 13 | 14 | # feasts 0.4.0 15 | 16 | ## New features 17 | 18 | * Added `gg_irf()` for plotting impulse responses (typically obtained from using 19 | `IRF()` with fable models). 20 | * Added cointegration tests `cointegration_johansen()` and 21 | `cointegration_phillips_ouliaris()` from `urca`. 22 | 23 | ## Improvements 24 | 25 | * Documentation improvements. 26 | 27 | ## Bug fixes 28 | 29 | * Fixed `gg_season()` not wrapping across `facet_period` argument correctly. 30 | 31 | # feasts 0.3.2 32 | 33 | Minor patch to resolve CRAN check issues with ggplot2 v3.5.0 breaking changes. 34 | 35 | ## Improvements 36 | 37 | * Calculate seasonally adjusted data from classical decomposition using original 38 | data and seasonal term rather than trend and remainder. 39 | 40 | ## Bug fixes 41 | 42 | * Fixed out-of-bounds `gg_season()` breaks issue with ggplot2 v3.5.0 43 | * Changed the metadata of classical decomposition's components to better reflect 44 | the seasonally adjusted variable's structure. 45 | 46 | # feasts 0.3.1 47 | 48 | Minor patch to resolve CRAN check issues with S3 method consistency. 49 | 50 | # feasts 0.3.0 51 | 52 | ## New features 53 | 54 | * Added the `tapered` argument to `ACF()` and `PACF()` for producing banded and 55 | tapered estimates of autocovariance (#1). 56 | 57 | ## Improvements 58 | 59 | * `gg_season()` now allows seasonal period identifying labels to be nudged and 60 | repelled with the `labels_repel`, `labels_left_nudge`, and 61 | `labels_right_nudge` arguments. 62 | * `gg_season()` behaviour of `max_col` has been restored, where colours aren't 63 | used if the number of subseries to be coloured exceeds this value. The default 64 | has changed to `Inf` since this function now supports continuous colour 65 | guides. A new argument `max_col_discrete` has been added to control the 66 | threshold for showing discrete and continuous colour guides (#150). 67 | * Updated `guerrero()` method to maintain a consistent subseries length by 68 | removing the first few observations of needed. This more closely matches 69 | the described method, and the implementation in the forecast package. 70 | * Added `grid.draw()` method for ensemble graphics (`gg_tsdisplay()` and 71 | `gg_tsresiduals()`). This allows use of `ggsave()` with these plots (#149). 72 | 73 | ## Bug fixes 74 | 75 | * Fixed `generate()` returning `$.sim` as a `num [1:n(1d)]` instead of 76 | `num [1:72]` (fable/#336). 77 | * Fixed issue with `gg_season()` incorrectly grouping some seasonal subseries. 78 | * `CCF()` now matches `stats::ccf()` `x` and `y` arguments (#144). 79 | 80 | # feasts 0.2.2 81 | 82 | Minor release for compatibility with an upcoming ggplot2 release. This release 83 | contains a few bug fixes and improvements to existing functionality. 84 | 85 | ## Improvements 86 | 87 | * The `gg_tsresiduals()` function now allows the type of plotted residual to be 88 | controlled via the `type` argument. 89 | * Improved the default seasonal window for `STL()` decompositions. For data with 90 | a single seasonal pattern, the window has changed from 13 to 11. This change 91 | is based on results from simulation experiments. 92 | * Documentation improvements. 93 | 94 | ## Bug fixes 95 | 96 | * Fixed issue where `seasonal::seas()` defaults were not being used in 97 | `X_13ARIMA_SEATS()` when `defaults = "seasonal"` (#130). 98 | * Fixed issue with `gg_subseries()` on data with spaces in the index column 99 | name (#136). 100 | 101 | ## Breaking changes 102 | 103 | * Replaced usage of `...` in `ACF()`, `PACF()`, and `CCF()` with `y` (and `x` 104 | for `CCF()`) arguments. This change should not affect the code for most users, 105 | but is important for the eventual passing of `...` to `acf()`, `pacf()` and 106 | `ccf()` in a future version (#124). 107 | 108 | # feasts 0.2.1 109 | 110 | Small patch to fix check issues on Solaris, and to resolve `components()` for 111 | automatically selected transformations in `X_13ARIMA_SEATS()`. 112 | 113 | # feasts 0.2.0 114 | 115 | ## New features 116 | 117 | * Added `X_13ARIMA_SEATS()` decomposition method. This is a complete wrapper of 118 | the X-13ARIMA-SEATS developed by the U.S. Census Bureau, implemented via 119 | the `seasonal::seas()` function. The defaults match what is used in the 120 | seasonal pacakge, however these defaults can be removed (giving an empty 121 | default model) by setting `defaults="none"`. 122 | 123 | ## Breaking changes 124 | * The new `X_13ARIMA_SEATS()` method officially deprecates (supersedes) the 125 | `X11()` and `SEATS()` models which were previously not exported (#66). 126 | 127 | ## Improvements 128 | 129 | * Documentation improvements. 130 | 131 | # feasts 0.1.7 132 | 133 | ## New features 134 | 135 | * Added `generate()` method for `STL()` decompositions. The method uses a block 136 | bootstrap method to sample from the residuals. 137 | * Added `fitted()` and `residuals()` methods for `STL()` decompositions. 138 | 139 | ## Improvements 140 | 141 | * Changed `guerrero()` default lower bound for Box-Cox lambda selection to from 142 | -1 to -0.9. A transformation parameter of -1 typically results from data which 143 | should not be transformed with a Box-Cox transformation, and can result in 144 | very inaccurate forecasts if such a strong and inappropriate transformation is 145 | used. 146 | * Improved time series plotting functions axis labelling. 147 | * Documentation improvements. 148 | 149 | # feasts 0.1.6 150 | 151 | A minor release to fix check issues introduced by changes in an upstream 152 | dependency. 153 | 154 | ## Improvements 155 | 156 | * `gg_season()` labels are low aligned outward (#115). 157 | 158 | ## Bug fixes 159 | 160 | * Fixed issue with plotting aggregated tsibbles with `gg_season()` and 161 | `gg_subseries()` (#117). 162 | * Fixed occasional issue with double label/breaks displayed in `gg_season()` 163 | 164 | 165 | # feasts 0.1.5 166 | 167 | ## Improvements 168 | 169 | * `gg_lag()` facets are now displayed with a 1:1 aspect ratio. 170 | * Season and subseries plots of numeric index data now starts at the earliest 171 | measured observation, rather than assuming a meaningful 0 (#111). 172 | * The `n_flat_spots()` function has been renamed to `longest_flat_spot()` to 173 | more accurately describe the feature. 174 | * `gg_season()` and `ggsubseries()` date structure improvements. 175 | * Documentation improvements 176 | 177 | ## Breaking changes 178 | 179 | * The `n_flat_spots()` return name is now "longest_flat_spot" to better describe 180 | the feature. 181 | 182 | ## Bug fixes 183 | 184 | * Fixed spectral density plot in `gg_tsdisplay()` erroring when the `spec.ar` 185 | order is chosen to be 0. 186 | * Fixed `CCF()` lag being spaced by multiples of the data's frequency. 187 | * Fixed labelling of x-axis for `gg_season()` and `gg_subseries()` (#107). 188 | * Fixed `View()` not working on `ACF()`, `PACF()` and `CCF()` outputs. 189 | 190 | # feasts 0.1.4 191 | 192 | Minor patch to resolve upstream check issues introduced by dplyr v1.0.0 and 193 | tsibble v0.9.0. 194 | 195 | ## New features 196 | 197 | * Circular time plots are now supported by setting `polar = TRUE` in 198 | `gg_season()`. 199 | 200 | ## Improvements 201 | 202 | * Added partial matching of the type argument in `ACF()`. 203 | * Updated `feat_spectral()` to use `stats::spec.ar()` instead of 204 | `ForeCA::spectral_entropy()`. Note that the feature value will be slightly 205 | different due to use of a different spectral estimator, and the fix of a 206 | bug in ForeCA. 207 | 208 | ## Bug fixes 209 | 210 | * Fixed the minimum data length for seasonal estimation in `feat_stl()`. 211 | 212 | # feasts 0.1.3 213 | 214 | ## Improvements 215 | 216 | * The axis for `gg_lag()` have been reversed for consistency with `stats::lag.plot()`. 217 | * Graphical improvements for displaying weekly seasonality in season and subseries plots. 218 | * Added intermittency features available in `feat_intermittent()` 219 | 220 | ## Bug fixes 221 | 222 | * Fixed the sprectral density plot in `gg_tsdisplay()` not working with plotting expressions of data. 223 | * Fixed issue with `gg_subseries()` erroring when certain column names are used (#95). 224 | * Fixed issue with environment handling in `STL()` specials. 225 | 226 | # feasts 0.1.2 227 | 228 | ## Improvements 229 | 230 | * `var_tiled_var()` no longer includes partial tile windows in the computation. 231 | * Added residual acf features to `feat_stl()`. 232 | * Performance improvements. 233 | 234 | ## Breaking changes 235 | 236 | * Decompositions are now treated as models. 237 | To access the decomposed values, you will now have to use `components()`. 238 | For example, `tourism %>% STL(Trips)` is now `tourism %>% model(STL(Trips)) %>% components()`. 239 | This change allows for more flexible decomposition specifications, and better interfaces for decomposition modelling. 240 | 241 | ## Bug fixes 242 | 243 | * Fixed bug with `feat_spectral()` not showing results. 244 | * Fix warning in `ACF()`, `PACF()` and `CCF()` for tidyr change. 245 | * `gg_tsdisplay()` will no longer fail on non-seasonal data with missing values. The last plot will instead show a PACF in this case (#76) 246 | * Better handling of perfect fits in `stat_arch_lm()` (#85) 247 | 248 | # feasts 0.1.1 249 | 250 | ## Improvements 251 | 252 | * Better naming of seasonal columns in STL decomposition when seasonal period is specified. 253 | 254 | ## Bug fixes 255 | 256 | * Fixes issues with running tests on unsupported systems. 257 | 258 | # feasts 0.1.0 259 | 260 | * First release. 261 | 262 | ## New features 263 | 264 | * Added support for graphical analysis of tidy temporal data and models, with `gg_season`, `gg_subseries`, `gg_lag`, `gg_tsdisplay`, `gg_tsresiduals`, `gg_arma`. 265 | * Added support for autocorrelation functions and plots, with `ACF`, `PACF`, `CCF`, and `autoplot.tbl_cf` 266 | * Added a collection of features to be used with `fabletools::features()`: `feat_stl`, `feat_acf`, `feat_pacf`, `guerrero`, `unitroot_kpss`, `unitroot_pp`, `unitroot_ndiffs`, `unitroot_nsdiffs`, `box_pierce`, `ljung_box`, `var_tiled_var`, `var_tiled_mean`, `shift_level_max`, `shift_var_max`, `shift_kl_max`, `feat_spectral`, `n_crossing_points`, `n_flat_spots`, `coef_hurst`, `stat_arch_lm` 267 | * Added support for two decomposition methods: `classical_decomposition`, `STL` 268 | -------------------------------------------------------------------------------- /R/acf.R: -------------------------------------------------------------------------------- 1 | #' (Partial) Autocorrelation and Cross-Correlation Function Estimation 2 | #' 3 | #' The function `ACF` computes an estimate of the autocorrelation function 4 | #' of a (possibly multivariate) tsibble. Function `PACF` computes an estimate 5 | #' of the partial autocorrelation function of a (possibly multivariate) tsibble. 6 | #' Function `CCF` computes the cross-correlation or cross-covariance of two columns 7 | #' from a tsibble. 8 | #' 9 | #' The functions improve the [`stats::acf()`], [`stats::pacf()`] and 10 | #' [`stats::ccf()`] functions. The main differences are that `ACF` does not plot 11 | #' the exact correlation at lag 0 when `type=="correlation"` and 12 | #' the horizontal axes show lags in time units rather than seasonal units. 13 | #' 14 | #' The resulting tables from these functions can also be plotted using 15 | #' [`autoplot.tbl_cf()`]. 16 | #' 17 | # The tapered versions implement the ACF and PACF estimates and plots 18 | # described in Hyndman (2015), based on the banded and tapered estimates of 19 | # autocovariance proposed by McMurry and Politis (2010). 20 | #' 21 | #' @param .data A tsibble 22 | #' @param ... The column(s) from the tsibble used to compute the ACF, PACF or CCF. 23 | #' @param lag_max maximum lag at which to calculate the acf. Default is 10*log10(N/m) 24 | #' where N is the number of observations and m the number of series. Will be 25 | #' automatically limited to one less than the number of observations in the series. 26 | #' @param type character string giving the type of ACF to be computed. Allowed values are `"correlation"` (the default), `"covariance"` or `"partial"`. 27 | #' @param tapered Produces banded and tapered estimates of the (partial) autocorrelation. 28 | #' @inheritParams stats::acf 29 | #' 30 | #' @return The `ACF`, `PACF` and `CCF` functions return objects 31 | #' of class "tbl_cf", which is a tsibble containing the correlations computed. 32 | #' 33 | #' @author Mitchell O'Hara-Wild and Rob J Hyndman 34 | #' 35 | #' @references Hyndman, R.J. (2015). Discussion of "High-dimensional 36 | #' autocovariance matrices and optimal linear prediction". \emph{Electronic 37 | #' Journal of Statistics}, 9, 792-796. 38 | #' 39 | #' McMurry, T. L., & Politis, D. N. (2010). Banded and tapered estimates for 40 | #' autocovariance matrices and the linear process bootstrap. \emph{Journal of 41 | #' Time Series Analysis}, 31(6), 471-482. 42 | #' 43 | #' @seealso [`stats::acf()`], [`stats::pacf()`], [`stats::ccf()`] 44 | #' 45 | #' @examples 46 | #' library(tsibble) 47 | #' library(tsibbledata) 48 | #' library(dplyr) 49 | #' 50 | #' vic_elec %>% ACF(Temperature) 51 | #' 52 | #' vic_elec %>% ACF(Temperature) %>% autoplot() 53 | #' 54 | #' @importFrom tibble tibble 55 | #' @importFrom stats as.ts frequency 56 | #' @importFrom utils tail 57 | #' @importFrom fabletools get_frequencies 58 | #' 59 | #' @rdname ACF 60 | #' @export 61 | ACF <- function(.data, y, ..., lag_max = NULL, 62 | type = c("correlation", "covariance", "partial"), 63 | na.action = na.contiguous, demean = TRUE, tapered = FALSE){ 64 | type <- match.arg(type) 65 | compute_acf <- function(.data, value, lag.max, ...){ 66 | value <- enexpr(value) 67 | x <- eval_tidy(value, data = .data) 68 | 69 | acf <- if(tapered) { 70 | tacf(x)[seq_len(lag_max+1)] 71 | } else { 72 | as.numeric(acf(x, plot=FALSE, lag.max=lag.max, ...)$acf) 73 | } 74 | 75 | if(type != "partial"){ # First indx already dropped if partial 76 | acf <- tail(acf, -1) 77 | } 78 | tibble(lag = seq_along(acf), acf = acf) 79 | } 80 | if(dots_n(...) > 0) { 81 | lifecycle::deprecate_warn( 82 | "0.2.2", "PACF(...)", details = "ACF variables should be passed to the `y` argument. If multiple variables are to be used, specify them using `vars(...)`." 83 | ) 84 | } 85 | value <- Filter(negate(quo_is_missing), enquos(y, ...)) 86 | if(length(value) == 0){ 87 | if(is_empty(measured_vars(.data))){ 88 | abort("There are no variables to compute the ACF.") 89 | } 90 | inform(sprintf( 91 | "Response variable not specified, automatically selected `var = %s`", 92 | measured_vars(.data)[1] 93 | )) 94 | value <- syms(measured_vars(.data)[1]) 95 | } 96 | if(length(value) > 1){ 97 | warn(sprintf("ACF currently only supports one column, `%s` will be used.", 98 | as_name(value[[1]]))) 99 | } 100 | build_cf(.data, compute_acf, value=!!value[[1]], lag.max = lag_max, 101 | demean = demean, type = type, na.action = na.action) 102 | } 103 | 104 | #' @rdname ACF 105 | #' @examples 106 | #' vic_elec %>% PACF(Temperature) 107 | #' 108 | #' vic_elec %>% PACF(Temperature) %>% autoplot() 109 | #' 110 | #' @export 111 | PACF <- function(.data, y, ..., lag_max = NULL, 112 | na.action = na.contiguous, 113 | tapered = FALSE){ 114 | compute_pacf <- function(.data, value, lag.max, ...){ 115 | value <- enexpr(value) 116 | x <- eval_tidy(value, data = .data) 117 | 118 | if (tapered) { 119 | # Compute acf using tapered estimate 120 | acvf <- tacf(x) 121 | # Durbin-Levinson recursions 122 | # Modified from http://faculty.washington.edu/dbp/s519/R-code/LD-recursions.R 123 | p <- length(acvf) - 1 124 | phis <- acvf[2] / acvf[1] 125 | pev <- rep(acvf[1], p + 1) 126 | pacf <- rep(phis, lag.max) 127 | pev[2] <- pev[1] * (1 - phis ^ 2) 128 | if (p > 1) { 129 | for (k in 2:lag.max) 130 | { 131 | old.phis <- phis 132 | phis <- rep(0, k) 133 | ## compute kth order pacf (reflection coefficient) 134 | phis[k] <- (acvf[k + 1] - sum(old.phis * acvf[k:2])) / pev[k] 135 | phis[1:(k - 1)] <- old.phis - phis[k] * rev(old.phis) 136 | pacf[k] <- phis[k] 137 | pev[k + 1] <- pev[k] * (1 - phis[k] ^ 2) 138 | # if(abs(pacf[k]) > 1) 139 | # warning("PACF larger than 1 in absolute value") 140 | } 141 | } 142 | } else { 143 | pacf <- as.numeric(pacf(x, plot=FALSE, lag.max = lag.max, ...)$acf) 144 | } 145 | 146 | tibble(lag = seq_along(pacf), pacf = pacf) 147 | } 148 | if(dots_n(...) > 0) { 149 | lifecycle::deprecate_warn( 150 | "0.2.2", "PACF(...)", details = "PACF variables should be passed to the `y` argument. If multiple variables are to be used, specify them using `vars(...)`." 151 | ) 152 | } 153 | value <- Filter(negate(quo_is_missing), enquos(y, ...)) 154 | if(length(value) == 0){ 155 | if(is_empty(measured_vars(.data))){ 156 | abort("There are no variables to compute the PACF.") 157 | } 158 | inform(sprintf( 159 | "Response variable not specified, automatically selected `var = %s`", 160 | measured_vars(.data)[1] 161 | )) 162 | value <- syms(measured_vars(.data)[1]) 163 | } 164 | if(length(value) > 1){ 165 | warn(sprintf("PACF currently only supports one column, `%s` will be used.", 166 | as_name(value[[1]]))) 167 | } 168 | build_cf(.data, compute_pacf, value=!!value[[1]], lag.max = lag_max, 169 | na.action = na.action) 170 | } 171 | 172 | #' @rdname ACF 173 | #' @examples 174 | #' global_economy %>% 175 | #' filter(Country == "Australia") %>% 176 | #' CCF(GDP, Population) 177 | #' 178 | #' global_economy %>% 179 | #' filter(Country == "Australia") %>% 180 | #' CCF(GDP, Population) %>% 181 | #' autoplot() 182 | #' 183 | #' @export 184 | CCF <- function(.data, y, x, ..., lag_max = NULL, 185 | type = c("correlation", "covariance"), 186 | na.action = na.contiguous){ 187 | compute_ccf <- function(.data, value1, value2, ...){ 188 | value1 <- enexpr(value1) 189 | value2 <- enexpr(value2) 190 | ccf <- ccf(y = eval_tidy(value1, data = .data), 191 | x = eval_tidy(value2, data = .data), 192 | plot=FALSE, ...) 193 | lag <- as.numeric(ccf$lag) 194 | tibble(lag = lag, ccf = as.numeric(ccf$acf)) 195 | } 196 | if(dots_n(...) > 0) { 197 | lifecycle::deprecate_warn( 198 | "0.2.2", "CCF(...)", details = "CCF variables should be passed to the `y` and `x` arguments. If multiple variables are to be used, specify them using `vars(...)`." 199 | ) 200 | } 201 | value <- Filter(negate(quo_is_missing), enquos(y, x, ...)) 202 | if(length(value) == 0){ 203 | if(length(measured_vars(.data)) < 2){ 204 | abort("CCF requires two columns to be specified.") 205 | } 206 | inform(sprintf( 207 | "Response variable not specified, automatically selected `%s` and `%s`", 208 | measured_vars(.data)[1], measured_vars(.data)[2] 209 | )) 210 | value <- syms(measured_vars(.data)[1:2]) 211 | } 212 | if(length(value) > 2){ 213 | warn(sprintf("CCF currently only supports two columns, `%s` and `%s` will be used.", 214 | as_name(value[[1]]), as_name(value[[2]]))) 215 | } 216 | if(length(value) == 1){ 217 | abort("CCF requires two columns to be specified.") 218 | } 219 | build_cf(.data, compute_ccf, value1=!!value[[1]], value2=!!value[[2]], 220 | lag.max = lag_max, type = type, na.action = na.action) 221 | } 222 | 223 | #' @importFrom stats na.contiguous 224 | build_cf <- function(.data, cf_fn, ...){ 225 | check_gaps(.data) 226 | if(is_regular(.data)){ 227 | interval <- interval(.data) 228 | } 229 | else{ 230 | warn("Provided data has an irregular interval, results should be treated with caution. Computing ACF by observation.") 231 | interval <- new_interval(unit = 1) 232 | } 233 | 234 | kv <- key_vars(.data) 235 | 236 | lens <- key_data(.data) %>% 237 | transmute( 238 | !!!key(.data), 239 | .len = map_dbl(!!sym(".rows"), length) 240 | ) 241 | 242 | .data <- nest_keys(.data) 243 | .data[["data"]] <- map(.data[["data"]], cf_fn, ...) 244 | .data <- unnest_tbl(.data, "data") 245 | .data[["lag"]] <- as_lag(interval) * .data[["lag"]] 246 | new_tsibble( 247 | as_tsibble(.data, index = "lag", key = !!kv), 248 | num_obs = lens, class = "tbl_cf" 249 | ) 250 | } 251 | 252 | tacf <- function(x) { 253 | acf <- as.numeric(acf(x, plot=FALSE, lag.max=length(x)-1)$acf) 254 | # Taper estimates 255 | s <- seq_along(acf) 256 | upper <- 2 * sqrt(log(length(x), 10) / length(x)) 257 | ac <- abs(acf) 258 | # Find l: ac < upper for 5 consecutive lags 259 | j <- (ac < upper) 260 | l <- 0 261 | k <- 1 262 | N <- length(j) - 4 263 | while (l < 1 && k <= N) { 264 | if (all(j[k:(k + 4)])) { 265 | l <- k 266 | } else { 267 | k <- k + 1 268 | } 269 | } 270 | sl <- s / l 271 | k <- numeric(length(sl)) 272 | k[sl <= 1] <- 1 273 | k[sl > 1 & sl <= 2] <- 2 - sl[sl > 1 & sl <= 2] 274 | 275 | acf <- acf * k 276 | # End of Tapering 277 | 278 | # Now do some shrinkage towards white noise using eigenvalues 279 | # Construct covariance matrix 280 | gamma <- acf 281 | s <- length(gamma) 282 | Gamma <- matrix(1, s, s) 283 | d <- row(Gamma) - col(Gamma) 284 | for (i in 1:(s - 1)) 285 | Gamma[d == i | d == (-i)] <- gamma[i + 1] 286 | # Compute eigenvalue decomposition 287 | ei <- eigen(Gamma) 288 | # Shrink eigenvalues 289 | d <- pmax(ei$values, 20 / length(x)) 290 | # Construct new covariance matrix 291 | Gamma2 <- ei$vectors %*% diag(d) %*% t(ei$vectors) 292 | Gamma2 <- Gamma2 / mean(d) 293 | # Estimate new ACF 294 | d <- row(Gamma2) - col(Gamma2) 295 | for (i in 2:s) 296 | gamma[i] <- mean(Gamma2[d == (i - 1)]) 297 | 298 | ############### end of shrinkage 299 | gamma 300 | } 301 | 302 | # Temporary until generic time class is available for temporal hierarchies 303 | as_lag <- function(x, ...) { 304 | UseMethod("as_lag") 305 | } 306 | 307 | #' @export 308 | as_lag.interval <- function(x, ...){ 309 | new_lag(1, x) 310 | } 311 | 312 | #' @export 313 | as_lag.default <- function(x, ...){ 314 | abort( 315 | sprintf("`as_lag()` doesn't know how to handle the '%s' class yet.", 316 | class(x)[1]) 317 | ) 318 | } 319 | 320 | new_lag <- function(x, interval){ 321 | vctrs::new_vctr(x, interval = interval, class = "cf_lag") 322 | } 323 | 324 | #' @export 325 | vec_arith.cf_lag <- function(op, x, y, ...){ 326 | out <- vctrs::vec_data(x) 327 | out <- get(op)(out, y) 328 | vctrs::vec_restore(out, x) 329 | } 330 | 331 | #' @export 332 | format.cf_lag <- function(x, ...){ 333 | interval <- attr(x, "interval") 334 | itvl_data <- if(inherits(interval, "vctrs_vctr")) vctrs::vec_data else unclass 335 | itvl_fmt <- utils::getS3method("format", "interval", envir = getNamespace("tsibble")) 336 | scale <- do.call(sum, itvl_data(interval)) 337 | suffix <- substring(itvl_fmt(interval), first = nchar(format(scale)) + 1) 338 | paste0(scale*vec_data(x), suffix) 339 | } 340 | 341 | #' @export 342 | vec_ptype_full.cf_lag <- function(x, ...){ 343 | "lag" 344 | } 345 | 346 | #' @importFrom vctrs vec_ptype2 347 | #' @method vec_ptype2 cf_lag 348 | #' @export 349 | vec_ptype2.cf_lag <- function(x, y, ...) UseMethod("vec_ptype2.cf_lag", y) 350 | #' @method vec_ptype2.cf_lag default 351 | #' @export 352 | vec_ptype2.cf_lag.default <- function(x, y, ..., x_arg = "x", y_arg = "y") { 353 | vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg) 354 | } 355 | #' @method vec_ptype2.cf_lag double 356 | #' @export 357 | vec_ptype2.cf_lag.double <- function(x, y, ..., x_arg = "x", y_arg = "y") { 358 | double() 359 | } 360 | #' @method vec_ptype2.double cf_lag 361 | #' @export 362 | vec_ptype2.double.cf_lag <- function(x, y, ..., x_arg = "x", y_arg = "y") { 363 | double() 364 | } 365 | 366 | #' @importFrom vctrs vec_cast 367 | #' @method vec_cast cf_lag 368 | #' @export 369 | vec_cast.cf_lag <- function(x, to, ...) UseMethod("vec_cast.cf_lag") 370 | #' @method vec_cast.cf_lag default 371 | #' @export 372 | vec_cast.cf_lag.default <- function(x, to, ...) vec_default_cast(x, to) 373 | #' @method vec_cast.cf_lag double 374 | #' @export 375 | vec_cast.cf_lag.double <- function(x, to, ...) vec_data(x) 376 | #' @method vec_cast.double cf_lag 377 | #' @export 378 | vec_cast.double.cf_lag <- function(x, to, ...) vec_data(x) 379 | #' @method vec_cast.character cf_lag 380 | #' @export 381 | vec_cast.character.cf_lag <- function(x, to, ...) format(x) 382 | 383 | #' @export 384 | index_valid.cf_lag <- function(x) TRUE 385 | 386 | #' @export 387 | interval_pull.cf_lag <- function(x) { 388 | attr(x, "interval") 389 | } 390 | 391 | #' Auto- and Cross- Covariance and -Correlation plots 392 | #' 393 | #' Produces an appropriate plot for the result of [`ACF()`], [`PACF()`], or [`CCF()`]. 394 | #' 395 | #' @param object A tbl_cf object (the result [`ACF()`], [`PACF()`], or [`CCF()`]). 396 | #' @param level The level of confidence for the blue dashed lines. 397 | #' @param ... Unused. 398 | #' 399 | #' @return A ggplot object showing the correlations. 400 | #' 401 | #' @importFrom ggplot2 ggplot geom_hline xlab ylab ggtitle vars 402 | #' @importFrom stats qnorm 403 | #' @export 404 | autoplot.tbl_cf <- function(object, level = 95, ...){ 405 | cf_type <- colnames(object)[colnames(object) %in% c("acf", "pacf", "ccf")] 406 | plot_aes <- eval_tidy(expr(ggplot2::aes(x = !!sym("lag"), y = !!sym(cf_type)))) 407 | interval <- interval(object) 408 | 409 | if(length(level) > 1){ 410 | abort("Only one confidence interval is currently supported for this autoplot.") 411 | } 412 | 413 | itvl_fmt <- utils::getS3method("format", "interval", envir = getNamespace("tsibble")) 414 | p <- ggplot(object, plot_aes) + 415 | geom_linecol() + 416 | geom_hline(yintercept = 0) + 417 | xlab(paste0("lag [", itvl_fmt(interval),"]")) 418 | 419 | if(!is.null(level)){ 420 | conf_int <- object%@%"num_obs" %>% 421 | mutate( 422 | upper = qnorm((1 + (level/100)) / 2) / sqrt(!!sym(".len")), 423 | lower = !!parse_expr("-upper") 424 | ) %>% 425 | gather("type", ".ci", !!!syms(c("upper", "lower"))) 426 | p <- p + geom_hline(aes(yintercept = !!sym(".ci"), group = !!sym("type")), 427 | data = conf_int, colour = "blue", linetype = "dashed") 428 | } 429 | 430 | if(n_keys(object) > 1){ 431 | p <- p + facet_grid(rows = vars(!!!key(object))) 432 | } 433 | 434 | p 435 | } 436 | 437 | #' @importFrom ggplot2 scale_type 438 | #' @export 439 | scale_type.cf_lag <- function(x) c("cf_lag", "continuous") 440 | 441 | #' lagged datetime scales 442 | #' This set of scales defines new scales for lagged time structures. 443 | #' 444 | #' @param ... Further arguments to be passed on to scale_x_continuous() 445 | #' 446 | #' @return A ggproto object inheriting from `Scale` 447 | #' 448 | #' @keywords internal 449 | #' @name scale_cf_lag 450 | #' @rdname scale_cf_lag 451 | #' @export 452 | scale_x_cf_lag <- function(...) { 453 | scale <- ggplot2::ggproto("ScaleContinuousLag", ggplot2::scale_x_continuous(...), 454 | train = function (self, x){ 455 | if (length(x) == 0) 456 | return() 457 | self$range$train(vctrs::vec_data(x)) 458 | if(!is.null(gap <- attr(x, "interval"))){ 459 | self$interval <- gap 460 | freq <- get_frequencies(NULL, gap, .auto = "all") 461 | self$frequency <- min(freq[freq>3]%empty%NA) 462 | } 463 | }, 464 | get_breaks = function(self, limits = self$get_limits()) { 465 | if(inherits(self$breaks, "waiver") && !is.na(self$frequency)){ 466 | freq <- self$frequency 467 | lags <- ceiling(limits[1]):floor(limits[2]) 468 | # nlags <- length(lags) 469 | # np <- nlags / freq 470 | self$breaks <- lags[lags%%(freq/2)==0] 471 | } 472 | ggplot2::ggproto_parent(ggplot2::ScaleContinuous, self)$get_breaks() 473 | }, 474 | get_labels = function(self, breaks = self$get_breaks()){ 475 | # if(inherits(self$labels, "waiver")){ 476 | # interval_type <- map_lgl(self$interval, ~.x!=0) 477 | # self$labels <- breaks 478 | # #suffix <- toupper(names(self$interval)[interval_type]) 479 | # #self$labels <- paste(breaks*as.numeric(self$interval[interval_type]), paste0(suffix, ifelse(breaks!=0, "S", ""))) 480 | # } 481 | ggplot2::ggproto_parent(ggplot2::ScaleContinuous, self)$get_labels() 482 | }) 483 | scale 484 | } 485 | -------------------------------------------------------------------------------- /R/attach.R: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/R/attach.R -------------------------------------------------------------------------------- /R/classical.R: -------------------------------------------------------------------------------- 1 | specials_classical <- fabletools::new_specials( 2 | season = function(period = NULL){ 3 | period <- get_frequencies(period, self$data, .auto = "smallest") 4 | 5 | if(length(period) > 1){ 6 | warn("Multiple seasonal decomposition is not supported by classical decomposition") 7 | } 8 | period[[1]] 9 | }, 10 | .required_specials = c("season") 11 | ) 12 | 13 | train_classical <- function(.data, formula, specials, 14 | type = c("additive", "multiplicative"), ...){ 15 | stopifnot(is_tsibble(.data)) 16 | 17 | type <- match.arg(type) 18 | 19 | resp <- measured_vars(.data) 20 | y <- .data[[resp]] 21 | m <- specials$season[[1]] 22 | 23 | dcmp_op <- switch(type, 24 | additive = "+", 25 | multiplicative = "*") 26 | cl_sadj <- call2(switch(dcmp_op, `+` = "-", "/"), sym(resp), sym("seasonal")) 27 | 28 | dcmp <- decompose(ts(y, frequency = m), type = type, ...)[c("trend", "seasonal", "random")] 29 | 30 | dcmp <- .data %>% 31 | mutate( 32 | !!!map(dcmp, as.numeric), 33 | season_adjust = !!cl_sadj 34 | ) 35 | 36 | seasonalities <- list( 37 | seasonal = list(period = m, base = switch(dcmp_op, `+` = 0, 1)) 38 | ) 39 | 40 | aliases <- list2( 41 | !!resp := reduce(syms(c("trend", "seasonal", "random")), 42 | function(x,y) call2(dcmp_op, x, y)), 43 | season_adjust = cl_sadj 44 | ) 45 | 46 | structure( 47 | list(decomposition = dcmp, 48 | response = resp, method = "Classical", 49 | seasons = seasonalities, aliases = aliases 50 | ), 51 | class = "classical_decomposition" 52 | ) 53 | } 54 | 55 | #' @importFrom fabletools components as_dable 56 | #' @export 57 | components.classical_decomposition <- function(object, ...){ 58 | as_dable(object[["decomposition"]], response = !!sym(object[["response"]]), 59 | method = object[["method"]], seasons = object[["seasons"]], 60 | aliases = object[["aliases"]]) 61 | } 62 | 63 | #' @importFrom fabletools model_sum 64 | #' @export 65 | model_sum.classical_decomposition <- function(x){ 66 | "DECOMPOSITION" 67 | } 68 | 69 | #' Classical Seasonal Decomposition by Moving Averages 70 | #' 71 | #' @inherit stats::decompose description details 72 | #' 73 | #' @param formula Decomposition specification (see "Specials" section). 74 | #' @param ... Other arguments passed to [`stats::decompose()`]. 75 | #' @inheritParams stats::decompose 76 | #' 77 | #' @return A [`fabletools::dable()`] containing the decomposed trend, seasonality 78 | #' and remainder from the classical decomposition. 79 | #' 80 | #' @section Specials: 81 | #' 82 | #' \subsection{season}{ 83 | #' The `season` special is used to specify seasonal attributes of the decomposition. 84 | #' \preformatted{ 85 | #' season(period = NULL) 86 | #' } 87 | #' 88 | #' \tabular{ll}{ 89 | #' `period` \tab The periodic nature of the seasonality. This can be either a number indicating the number of observations in each seasonal period, or text to indicate the duration of the seasonal window (for example, annual seasonality would be "1 year"). 90 | #' } 91 | #' } 92 | #' 93 | #' @examples 94 | #' as_tsibble(USAccDeaths) %>% 95 | #' model(classical_decomposition(value)) %>% 96 | #' components() 97 | #' 98 | #' as_tsibble(USAccDeaths) %>% 99 | #' model(classical_decomposition(value ~ season(12), type = "mult")) %>% 100 | #' components() 101 | #' 102 | #' @importFrom stats ts decompose 103 | #' @importFrom fabletools new_model_class new_model_definition 104 | #' @export 105 | classical_decomposition <- function(formula, type = c("additive", "multiplicative"), ...){ 106 | dcmp <- new_model_class("Classical decomposition", 107 | train = train_classical, specials = specials_classical, 108 | check = all_tsbl_checks) 109 | new_model_definition(dcmp, !!enquo(formula), type = type, ...) 110 | } 111 | -------------------------------------------------------------------------------- /R/compact-purrr.R: -------------------------------------------------------------------------------- 1 | # nocov start 2 | 3 | # This file serves as a reference for compatibility functions for 4 | # purrr. They are not drop-in replacements but allow a similar style 5 | # of programming. This is useful in cases where purrr is too heavy a 6 | # package to depend on. 7 | 8 | # Slightly adapted from the version found in rlang 9 | 10 | map <- function(.x, .f, ...) { 11 | lapply(.x, .f, ...) 12 | } 13 | map_mold <- function(...) { 14 | out <- vapply(..., USE.NAMES = FALSE) 15 | names(out) <- names(..1) 16 | out 17 | } 18 | map_lgl <- function(.x, .f, ...) { 19 | map_mold(.x, .f, logical(1), ...) 20 | } 21 | map_int <- function(.x, .f, ...) { 22 | map_mold(.x, .f, integer(1), ...) 23 | } 24 | map_dbl <- function(.x, .f, ...) { 25 | map_mold(.x, .f, double(1), ...) 26 | } 27 | map_chr <- function(.x, .f, ...) { 28 | map_mold(.x, .f, character(1), ...) 29 | } 30 | map_cpl <- function(.x, .f, ...) { 31 | map_mold(.x, .f, complex(1), ...) 32 | } 33 | 34 | pluck <- function(.x, .f) { 35 | map(.x, `[[`, .f) 36 | } 37 | pluck_lgl <- function(.x, .f) { 38 | map_lgl(.x, `[[`, .f) 39 | } 40 | pluck_int <- function(.x, .f) { 41 | map_int(.x, `[[`, .f) 42 | } 43 | pluck_dbl <- function(.x, .f) { 44 | map_dbl(.x, `[[`, .f) 45 | } 46 | pluck_chr <- function(.x, .f) { 47 | map_chr(.x, `[[`, .f) 48 | } 49 | pluck_cpl <- function(.x, .f) { 50 | map_cpl(.x, `[[`, .f) 51 | } 52 | 53 | map2 <- function(.x, .y, .f, ...) { 54 | mapply(.f, .x, .y, MoreArgs = list(...), SIMPLIFY = FALSE) 55 | } 56 | map2_lgl <- function(.x, .y, .f, ...) { 57 | as.vector(map2(.x, .y, .f, ...), "logical") 58 | } 59 | map2_int <- function(.x, .y, .f, ...) { 60 | as.vector(map2(.x, .y, .f, ...), "integer") 61 | } 62 | map2_dbl <- function(.x, .y, .f, ...) { 63 | as.vector(map2(.x, .y, .f, ...), "double") 64 | } 65 | map2_chr <- function(.x, .y, .f, ...) { 66 | as.vector(map2(.x, .y, .f, ...), "character") 67 | } 68 | map2_cpl <- function(.x, .y, .f, ...) { 69 | as.vector(map2(.x, .y, .f, ...), "complex") 70 | } 71 | 72 | args_recycle <- function(args) { 73 | lengths <- map_int(args, length) 74 | n <- max(lengths) 75 | 76 | stopifnot(all(lengths == 1L | lengths == n)) 77 | to_recycle <- lengths == 1L 78 | args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n)) 79 | 80 | args 81 | } 82 | pmap <- function(.l, .f, ...) { 83 | args <- args_recycle(.l) 84 | do.call("mapply", c( 85 | FUN = list(quote(.f)), 86 | args, MoreArgs = quote(list(...)), 87 | SIMPLIFY = FALSE, USE.NAMES = FALSE 88 | )) 89 | } 90 | 91 | probe <- function(.x, .p, ...) { 92 | if (is_logical(.p)) { 93 | stopifnot(length(.p) == length(.x)) 94 | .p 95 | } else { 96 | map_lgl(.x, .p, ...) 97 | } 98 | } 99 | 100 | keep <- function(.x, .f, ...) { 101 | .x[probe(.x, .f, ...)] 102 | } 103 | discard <- function(.x, .p, ...) { 104 | sel <- probe(.x, .p, ...) 105 | .x[is.na(sel) | !sel] 106 | } 107 | map_if <- function(.x, .p, .f, ...) { 108 | matches <- probe(.x, .p) 109 | .x[matches] <- map(.x[matches], .f, ...) 110 | .x 111 | } 112 | 113 | compact <- function(.x) { 114 | Filter(length, .x) 115 | } 116 | 117 | transpose <- function(.l) { 118 | inner_names <- names(.l[[1]]) 119 | 120 | result <- map(seq_along(.l[[1]]), function(i) { 121 | map(.l, .subset2, i) 122 | }) 123 | 124 | set_names(result, inner_names) 125 | } 126 | 127 | every <- function(.x, .p, ...) { 128 | for (i in seq_along(.x)) { 129 | if (!rlang::is_true(.p(.x[[i]], ...))) return(FALSE) 130 | } 131 | TRUE 132 | } 133 | some <- function(.x, .p, ...) { 134 | for (i in seq_along(.x)) { 135 | if (rlang::is_true(.p(.x[[i]], ...))) return(TRUE) 136 | } 137 | FALSE 138 | } 139 | negate <- function(.p) { 140 | function(...) !.p(...) 141 | } 142 | 143 | reduce <- function(.x, .f, ..., .init) { 144 | f <- function(x, y) .f(x, y, ...) 145 | Reduce(f, .x, init = .init) 146 | } 147 | reduce_right <- function(.x, .f, ..., .init) { 148 | f <- function(x, y) .f(y, x, ...) 149 | Reduce(f, .x, init = .init, right = TRUE) 150 | } 151 | accumulate <- function(.x, .f, ..., .init) { 152 | f <- function(x, y) .f(x, y, ...) 153 | Reduce(f, .x, init = .init, accumulate = TRUE) 154 | } 155 | accumulate_right <- function(.x, .f, ..., .init) { 156 | f <- function(x, y) .f(y, x, ...) 157 | Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) 158 | } 159 | 160 | invoke <- function(.f, .x, ..., .env = NULL){ 161 | .env <- .env %||% parent.frame() 162 | args <- c(as.list(.x), list(...)) 163 | do.call(.f, args, envir = .env) 164 | } 165 | imap <- function(.x, .f, ...){ 166 | map2(.x, names(.x) %||% seq_along(.x), .f, ...) 167 | } 168 | 169 | capture_error <- function (code, otherwise = NULL, quiet = TRUE) 170 | { 171 | tryCatch(list(result = code, error = NULL), error = function(e) { 172 | if (!quiet) 173 | message("Error: ", e$message) 174 | list(result = otherwise, error = e) 175 | }, interrupt = function(e) { 176 | stop("Terminated by user", call. = FALSE) 177 | }) 178 | } 179 | safely <- function (.f, otherwise = NULL, quiet = TRUE) 180 | { 181 | function(...) capture_error(.f(...), otherwise, quiet) 182 | } 183 | possibly <- function (.f, otherwise, quiet = TRUE) 184 | { 185 | force(otherwise) 186 | function(...) capture_error(.f(...), otherwise, quiet)$result 187 | } 188 | compose <- function (...) { 189 | fs <- lapply(list(...), match.fun) 190 | n <- length(fs) 191 | last <- fs[[n]] 192 | rest <- fs[-n] 193 | function(...) { 194 | out <- last(...) 195 | for (f in rev(rest)) { 196 | out <- f(out) 197 | } 198 | out 199 | } 200 | } 201 | # nocov end 202 | -------------------------------------------------------------------------------- /R/dcmp_checks.R: -------------------------------------------------------------------------------- 1 | check_gaps <- function(x){ 2 | if (any(has_gaps(x)$.gaps)) { 3 | abort(sprintf("%s contains implicit gaps in time. You should check your data and convert implicit gaps into explicit missing values using `tsibble::fill_gaps()` if required.", deparse(substitute(x)))) 4 | } 5 | } 6 | 7 | check_regular <- function(x){ 8 | if (!is_regular(x)) { 9 | abort(sprintf("%s is an irregular time series, which this decomposition does not support. You should consider if your data can be made regular, and use `tsibble::update_tsibble(%s, regular = TRUE)` if appropriate.", deparse(substitute(x)), deparse(substitute(x)))) 10 | } 11 | } 12 | 13 | check_ordered <- function(x){ 14 | if (!is_ordered(x)) { 15 | abort(sprintf("%s is an unordered time series. To use this decomposition, you first must sort the data in time order using `dplyr::arrange(%s, %s)`", 16 | deparse(substitute(x)), paste(c(deparse(substitute(x)), key_vars(x)), collapse = ", "), as_string(index(x)))) 17 | } 18 | } 19 | 20 | all_tsbl_checks <- function(.data){ 21 | check_gaps(.data) 22 | check_regular(.data) 23 | check_ordered(.data) 24 | if(NROW(.data) == 0){ 25 | abort("There is no data to decompose. Please provide a dataset with at least one observation.") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /R/feasts.R: -------------------------------------------------------------------------------- 1 | #' @keywords package 2 | "_PACKAGE" 3 | 4 | globalVariables(".") 5 | 6 | #' @import rlang 7 | #' @import tsibble 8 | #' @import vctrs 9 | #' @importFrom dplyr mutate transmute summarise group_by ungroup filter distinct lead 10 | #' @importFrom tidyr gather 11 | NULL 12 | 13 | -------------------------------------------------------------------------------- /R/gg_geom.R: -------------------------------------------------------------------------------- 1 | GeomLineCol <- ggplot2::ggproto(NULL, ggplot2::GeomSegment, 2 | required_aes = c("x", "y"), 3 | setup_data = function(data, params){ 4 | transform(data, xend = x, yend = 0) 5 | }, 6 | draw_panel = function(self, data, panel_params, coord){ 7 | ggplot2::ggproto_parent(ggplot2::GeomSegment, self)$ 8 | draw_panel(data, panel_params, coord) 9 | }) 10 | 11 | #' @importFrom ggplot2 layer 12 | geom_linecol <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity", 13 | ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 14 | { 15 | layer(data = data, mapping = mapping, stat = stat, geom = GeomLineCol, 16 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 17 | params = list(na.rm = na.rm, ...)) 18 | } 19 | -------------------------------------------------------------------------------- /R/guerrero.R: -------------------------------------------------------------------------------- 1 | 2 | #' Guerrero's method for Box Cox lambda selection 3 | #' 4 | #' Applies Guerrero's (1993) method to select the lambda which minimises the 5 | #' coefficient of variation for subseries of x. 6 | #' 7 | #' Note that this function will give slightly different results to 8 | #' `forecast::BoxCox.lambda(y)` if your data does not start at the start of the 9 | #' seasonal period. This function will make use of all of your data, whereas the 10 | #' forecast package will not use data that doesn't complete a seasonal period. 11 | #' 12 | #' @param x A numeric vector. The data used to identify the transformation 13 | #' parameter lambda. 14 | #' @param lower The lower bound for lambda. 15 | #' @param upper The upper bound for lambda. 16 | #' @param .period The length of each subseries (usually the length of seasonal 17 | #' period). Subseries length must be at least 2. 18 | #' 19 | #' @return A Box Cox transformation parameter (lambda) chosen by Guerrero's method. 20 | #' 21 | #' @importFrom stats optimise 22 | #' 23 | #' @references 24 | #' 25 | #' Box, G. E. P. and Cox, D. R. (1964) An analysis of transformations. JRSS B 26 211–246. 26 | #' 27 | #' Guerrero, V.M. (1993) Time-series analysis supported by power transformations. Journal of Forecasting, 12, 37–48. 28 | #' 29 | #' @export 30 | guerrero <- function(x, lower = -0.9, upper = 2, .period = 2L) { 31 | if(all(x == x[1])) return(c(lambda_guerrero = 1)) 32 | 33 | # Prepare seasonal subseries 34 | .period <- max(2L, .period) 35 | n_obs <- length(x) 36 | n_subseries <- floor(n_obs/.period) 37 | x <- matrix( 38 | x[(n_obs - floor(n_subseries * .period) + 1):n_obs], 39 | nrow = .period, ncol = n_subseries 40 | ) 41 | 42 | c(lambda_guerrero = optimise( 43 | lambda_coef_var, c(lower, upper), x = x, 44 | .period = max(.period, 2) 45 | )$minimum) 46 | } 47 | 48 | # #' @param lambda Box-cox transformation parameter 49 | # #' @param x A vector 50 | # #' @param .period The length of each subseries (usually the length of seasonal period) 51 | #' @importFrom stats sd 52 | lambda_coef_var <- function(lambda, x, .period = 2) { 53 | # Mean of each subseries 54 | mu_h <- apply(x, 2, mean, na.rm = TRUE) 55 | # Standard deviation of each subseries 56 | sig_h <- apply(x, 2, sd, na.rm = TRUE) 57 | # Compute fitness metric 58 | rat <- sig_h / mu_h ^ (1 - lambda) 59 | sd(rat, na.rm = TRUE) / mean(rat, na.rm = TRUE) 60 | } 61 | -------------------------------------------------------------------------------- /R/reexports.R: -------------------------------------------------------------------------------- 1 | #' @importFrom dplyr %>% 2 | #' @export 3 | dplyr::`%>%` 4 | 5 | #' @export 6 | tsibble::as_tsibble 7 | 8 | #' @importFrom ggplot2 autoplot 9 | #' @export 10 | ggplot2::autoplot 11 | 12 | #' @importFrom ggplot2 autolayer 13 | #' @export 14 | ggplot2::autolayer 15 | -------------------------------------------------------------------------------- /R/seats.R: -------------------------------------------------------------------------------- 1 | globalVariables("self") 2 | 3 | specials_seats <- fabletools::new_specials( 4 | season = function(period = NULL){ 5 | period <- get_frequencies(period, self$data, .auto = "smallest") 6 | if(!(period %in% c(1, 2, 4, 6, 12))){ 7 | abort("The X-13ARIMA-SEATS method only supports seasonal patterns with seasonal periods of 1, 2, 4, 6 or 12. Defaulting to `period = 1`.") 8 | } 9 | period 10 | }, 11 | 12 | xreg = function(...){ 13 | abort("Exogenous regressors are not supported for X-13ARIMA-SEATS decompositions.") 14 | }, 15 | 16 | .required_specials = "season" 17 | ) 18 | 19 | train_seats <- function(.data, formula, specials, x11, x11.mode, ...){ 20 | require_package("seasonal") 21 | stopifnot(is_tsibble(.data)) 22 | 23 | if(!missing(x11) || !missing(x11.mode)){ 24 | abort("Use `X11()` to perform an X11 decomposition.") 25 | } 26 | 27 | if(length(specials$season) != 1){ 28 | abort("X-13ARIMA-SEATS only supports one seasonal period.") 29 | } 30 | period <- specials$season[[1]] 31 | y <- as.ts(.data, frequency = period) 32 | 33 | fit <- seasonal::seas(y, ...) 34 | 35 | dcmp <- unclass(fit$data) 36 | 37 | dcmp <- .data %>% 38 | mutate( 39 | trend = dcmp[,"trend"], 40 | seasonal = dcmp[,"adjustfac"], 41 | irregular = dcmp[,"irregular"], 42 | season_adjust = !!sym("trend") * !!sym("irregular") 43 | ) 44 | 45 | seasonalities <- list( 46 | seasonal = list(period = period, base = 1) 47 | ) 48 | 49 | aliases <- list2( 50 | !!measured_vars(.data) := reduce(syms(c("trend", "seasonal", "irregular")), 51 | function(x,y) call2("*", x, y)), 52 | season_adjust = call2("*", sym("trend"), sym("irregular")) 53 | ) 54 | 55 | structure( 56 | list(decomposition = dcmp, 57 | response = measured_vars(.data), method = "X-13ARIMA-SEATS", 58 | seasons = seasonalities, aliases = aliases 59 | ), 60 | class = "x13_decomposition" 61 | ) 62 | } 63 | 64 | #' @importFrom fabletools components as_dable 65 | #' @export 66 | components.x13_decomposition <- function(object, ...){ 67 | as_dable(object[["decomposition"]], response = !!sym(object[["response"]]), 68 | method = object[["method"]], seasons = object[["seasons"]], 69 | aliases = object[["aliases"]]) 70 | } 71 | 72 | #' @importFrom fabletools model_sum 73 | model_sum.x13_decomposition <- function(x){ 74 | "SEATS" 75 | } 76 | 77 | SEATS <- function(formula, ...){ 78 | lifecycle::deprecate_warn("0.2.0", "feasts::SEATS()", "feasts::X_13ARIMA_SEATS()", "You can specify the SEATS decomposition method by including seats() in the model formula of `X_13ARIMA_SEATS()`.") 79 | dcmp <- new_model_class("SEATS", train = train_seats, 80 | specials = specials_seats, check = all_tsbl_checks) 81 | new_model_definition(dcmp, !!enquo(formula), ...) 82 | } 83 | 84 | -------------------------------------------------------------------------------- /R/specials.R: -------------------------------------------------------------------------------- 1 | #' @importFrom stats model.frame 2 | model_xreg <- function(...){ 3 | model_formula <- new_formula( 4 | lhs = NULL, 5 | rhs = reduce(enexprs(...), function(.x, .y) call2("+", .x, .y)) 6 | ) 7 | model.frame(model_formula, data = self$data, na.action = stats::na.pass) 8 | } 9 | -------------------------------------------------------------------------------- /R/stl.R: -------------------------------------------------------------------------------- 1 | globalVariables("self") 2 | 3 | specials_stl <- fabletools::new_specials( 4 | trend = function(window, degree, jump){ 5 | args <- as.list(environment()) 6 | args <- args[!map_lgl(args, rlang::is_missing)] 7 | 8 | if(isFALSE(is.finite(args$window)) && sign(args$window) == 1){ 9 | args$window <- NROW(self$data) + 1e7 10 | } 11 | if(length(args) > 0){ 12 | set_names(args, paste0("t.", names(args))) 13 | } 14 | }, 15 | season = function(period = NULL, window = NULL, degree, jump){ 16 | args <- as.list(environment()) 17 | args <- args[!map_lgl(args, rlang::is_missing)] 18 | args <- args[names(args)!="period"] 19 | 20 | if(isFALSE(is.finite(args$window)) && is.numeric(args$window)){ 21 | if(sign(args$window) == 1){ 22 | args$window <- "periodic" 23 | } 24 | } 25 | args <- set_names(args, paste0("s.", names(args))) 26 | 27 | nm <- period 28 | period <- get_frequencies(period, self$data, .auto = "all") 29 | if(is.null(names(period))) names(period) <- nm 30 | period <- period[NROW(self$data)/period >= 2] 31 | if(!is.null(period)){ 32 | map(period, function(.x) c(period = .x, args)) 33 | } 34 | }, 35 | lowpass = function(window, degree, jump){ 36 | args <- as.list(environment()) 37 | args <- args[!map_lgl(args, rlang::is_missing)] 38 | 39 | if(isFALSE(is.finite(args$window)) && sign(args$window) == 1){ 40 | args$window <- NROW(self$data) + 1e7 41 | } 42 | if(length(args) > 0){ 43 | set_names(args, paste0("l.", names(args))) 44 | } 45 | }, 46 | 47 | .required_specials = c("trend", "season", "lowpass") 48 | ) 49 | 50 | estimate_stl <- function(y, trend.args, season.args, lowpass.args, 51 | iterations = 2, ...){ 52 | if(any(is.na(y))){ 53 | abort("STL decomposition does not support series with missing values.") 54 | } 55 | deseas <- y 56 | season.args <- Filter(function(x) x[["period"]] > 1, season.args) 57 | period <- map_dbl(season.args, function(x) x[["period"]]) 58 | season.args <- map2(season.args, 7 + 4*order(period), 59 | function(x, default_window){ 60 | x$s.window <- x$s.window %||% default_window 61 | x 62 | }) 63 | season.args <- season.args[order(period)] 64 | seas <- set_names(as.list(rep(0, length(season.args))), 65 | sprintf("season_%s", names(season.args)%||%map(season.args, function(x) x[["period"]]))) 66 | if(length(season.args) > 0){ 67 | for (j in seq_len(iterations)) 68 | { 69 | for (i in seq_along(season.args)) 70 | { 71 | deseas <- ts(deseas + seas[[i]], frequency = season.args[[i]][[1]]) 72 | fit <- eval_tidy(expr(stl(deseas, !!!c(trend.args, season.args[[i]][-1], lowpass.args), ...))) 73 | seas[[i]] <- as.numeric(fit$time.series[, "seasonal"]) 74 | deseas <- deseas - seas[[i]] 75 | } 76 | } 77 | trend <- fit$time.series[, "trend"] 78 | } 79 | else{ 80 | trend <- stats::supsmu(seq_along(y), y)$y 81 | } 82 | 83 | trend <- as.numeric(trend) 84 | deseas <- as.numeric(deseas) 85 | list2(trend = trend, !!!seas, remainder = deseas - trend, season_adjust = deseas) 86 | } 87 | 88 | train_stl <- function(.data, formula, specials, iterations = 2, ...){ 89 | stopifnot(is_tsibble(.data)) 90 | 91 | y <- .data[[measured_vars(.data)]] 92 | 93 | trend.args <- specials$trend[[1]] 94 | season.args <- unlist(specials$season, recursive = FALSE) 95 | season.args <- Filter(function(x) x[["period"]] > 1, season.args) 96 | lowpass.args <- specials$lowpass[[1]] 97 | 98 | decomposition <- .data %>% 99 | mutate(!!!estimate_stl(y, trend.args, season.args, lowpass.args, ...)) 100 | 101 | seas_cols <- sprintf("season_%s", names(season.args)%||%map(season.args, function(x) x[["period"]])) 102 | seasonalities <- lapply(season.args, function(x){ 103 | x["base"] <- 0 104 | x[c("period", "base")] 105 | }) 106 | names(seasonalities) <- seas_cols 107 | 108 | aliases <- list2( 109 | !!measured_vars(.data) := reduce(syms(c("trend", seas_cols, "remainder")), 110 | function(x,y) call2("+", x, y)), 111 | season_adjust = call2("+", sym("trend"), sym("remainder")) 112 | ) 113 | 114 | structure( 115 | list(decomposition = decomposition, 116 | response = measured_vars(.data), method = "STL", 117 | seasons = seasonalities, aliases = aliases 118 | ), 119 | class = "stl_decomposition" 120 | ) 121 | } 122 | 123 | #' @importFrom fabletools components as_dable 124 | #' @export 125 | components.stl_decomposition <- function(object, ...){ 126 | as_dable(object[["decomposition"]], response = !!sym(object[["response"]]), 127 | method = object[["method"]], seasons = object[["seasons"]], 128 | aliases = object[["aliases"]]) 129 | } 130 | 131 | #' @export 132 | fitted.stl_decomposition <- function(object, ...) { 133 | object[["decomposition"]][[object[["response"]]]] - residuals(object) 134 | } 135 | 136 | #' @importFrom stats residuals 137 | #' @export 138 | residuals.stl_decomposition <- function(object, ...) { 139 | object[["decomposition"]][["remainder"]] 140 | } 141 | 142 | MBB <- function (x, window_size) { 143 | bx <- array(0, (floor(length(x)/window_size) + 2) * window_size) 144 | for (i in 1:(floor(length(x)/window_size) + 2)) { 145 | c <- sample(1:(length(x) - window_size + 1), 1) 146 | bx[((i - 1) * window_size + 1):(i * window_size)] <- x[c:(c + window_size - 1)] 147 | } 148 | start_from <- sample(0:(window_size - 1), 1) + 1 149 | bx[start_from:(start_from + length(x) - 1)] 150 | } 151 | 152 | #' Generate block bootstrapped series from an STL decomposition 153 | #' 154 | #' Produces new data with the same structure by resampling the residuals using 155 | #' a block bootstrap procedure. This method can only generate within sample, and 156 | #' any generated data out of the trained sample will produce NA simulations. 157 | #' 158 | #' @inheritParams fable::generate.ARIMA 159 | #' 160 | #' @examples 161 | #' as_tsibble(USAccDeaths) %>% 162 | #' model(STL(log(value))) %>% 163 | #' generate(as_tsibble(USAccDeaths), times = 3) 164 | #' 165 | #' @references 166 | #' Bergmeir, C., R. J. Hyndman, and J. M. Benitez (2016). Bagging Exponential Smoothing Methods using STL Decomposition and Box-Cox Transformation. International Journal of Forecasting 32, 303-312. 167 | #' 168 | #' @importFrom fabletools generate 169 | #' 170 | #' @export 171 | generate.stl_decomposition <- function(x, new_data, specials = NULL, ...){ 172 | dcmp <- x$decomposition 173 | 174 | # Match new_data index with dcmp index 175 | pos <- vec_match(new_data[[index_var(new_data)]], dcmp[[index_var(dcmp)]]) 176 | 177 | if(!(".innov" %in% names(new_data))){ 178 | # Block bootstrap for each replicate 179 | kr <- tsibble::key_rows(new_data) 180 | 181 | # Get default bootstrap params 182 | period <- max(vapply(x$seasons, `[[`, double(1L), "period")) 183 | block_size <- ifelse(period > 1, 2 * period, min(8, floor(length(x)/2))) 184 | 185 | # Block bootstrap 186 | innov <- lapply(seq_along(kr), function(...) MBB(dcmp[["remainder"]], block_size)) 187 | innov <- mapply(function(i, e) e[pos[i]], kr, innov, SIMPLIFY = FALSE) 188 | new_data$.innov <- vec_c(!!!innov) 189 | } 190 | 191 | dcmp <- as_tibble(dcmp)[pos,] 192 | dcmp$remainder <- new_data$.innov 193 | 194 | new_data[[".sim"]] <- as.numeric(eval_tidy(x$aliases[[x$response]], dcmp)) 195 | new_data[[".innov"]] <- NULL 196 | new_data 197 | } 198 | 199 | #' @importFrom fabletools model_sum 200 | #' @export 201 | model_sum.stl_decomposition <- function(x){ 202 | "STL" 203 | } 204 | 205 | #' Multiple seasonal decomposition by Loess 206 | #' 207 | #' @inherit forecast::mstl 208 | #' 209 | #' @inheritParams classical_decomposition 210 | #' @param iterations Number of iterations to use to refine the seasonal component. 211 | #' @param ... Other arguments passed to [stats::stl()]. 212 | #' 213 | #' @return A [`fabletools::dable()`] containing the decomposed trend, seasonality 214 | #' and remainder from the STL decomposition. 215 | #' 216 | #' @section Specials: 217 | #' 218 | #' \subsection{trend}{ 219 | #' The `trend` special is used to specify the trend extraction parameters. 220 | #' \preformatted{ 221 | #' trend(window, degree, jump) 222 | #' } 223 | #' 224 | #' \tabular{ll}{ 225 | #' `window` \tab The span (in lags) of the loess window, which should be odd. If NULL, the default, nextodd(ceiling((1.5*period) / (1-(1.5/s.window)))), is taken.\cr 226 | #' `degree` \tab The degree of locally-fitted polynomial. Should be zero or one. \cr 227 | #' `jump` \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every `jump`th value. 228 | #' } 229 | #' } 230 | #' 231 | #' \subsection{season}{ 232 | #' The `season` special is used to specify the season extraction parameters. 233 | #' \preformatted{ 234 | #' season(period = NULL, window = NULL, degree, jump) 235 | #' } 236 | #' 237 | #' \tabular{ll}{ 238 | #' `period` \tab The periodic nature of the seasonality. This can be either a number indicating the number of observations in each seasonal period, or text to indicate the duration of the seasonal window (for example, annual seasonality would be "1 year").\cr 239 | #' `window` \tab The span (in lags) of the loess window, which should be odd. If the `window` is set to `"periodic"` or `Inf`, the seasonal pattern will be fixed. The window size should be odd and at least 7, according to Cleveland et al. The default (NULL) will choose an appropriate default, for a dataset with one seasonal pattern this would be 11, the second larger seasonal window would be 15, then 19, 23, ... onwards.\cr 240 | #' `degree` \tab The degree of locally-fitted polynomial. Should be zero or one. \cr 241 | #' `jump` \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every `jump`th value. 242 | #' } 243 | #' } 244 | #' 245 | #' \subsection{lowpass}{ 246 | #' The `lowpass` special is used to specify the low-pass filter parameters. 247 | #' \preformatted{ 248 | #' lowpass(window, degree, jump) 249 | #' } 250 | #' 251 | #' \tabular{ll}{ 252 | #' `window` \tab The span (in lags) of the loess window of the low-pass filter used for each subseries. Defaults to the smallest odd integer greater than or equal to the seasonal `period` which is recommended since it prevents competition between the trend and seasonal components. If not an odd integer its given value is increased to the next odd one. \cr 253 | #' `degree` \tab The degree of locally-fitted polynomial. Must be zero or one. \cr 254 | #' `jump` \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every `jump`th value. 255 | #' } 256 | #' } 257 | #' 258 | #' @references 259 | #' R. B. Cleveland, W. S. Cleveland, J.E. McRae, and I. Terpenning (1990) STL: A Seasonal-Trend Decomposition Procedure Based on Loess. Journal of Official Statistics, 6, 3–73. 260 | #' 261 | #' @examples 262 | #' as_tsibble(USAccDeaths) %>% 263 | #' model(STL(value ~ trend(window = 10))) %>% 264 | #' components() 265 | #' 266 | #' @importFrom stats ts stl 267 | #' @importFrom fabletools new_model_class new_model_definition 268 | #' @export 269 | STL <- function(formula, iterations = 2, ...){ 270 | dcmp <- new_model_class("STL", 271 | train = train_stl, specials = specials_stl, 272 | check = all_tsbl_checks) 273 | new_model_definition(dcmp, !!enquo(formula), iterations = iterations, ...) 274 | } 275 | 276 | -------------------------------------------------------------------------------- /R/tests.R: -------------------------------------------------------------------------------- 1 | #' Portmanteau tests 2 | #' 3 | #' Compute the Box–Pierce or Ljung–Box test statistic for examining the null hypothesis of independence in a given time series. These are sometimes known as ‘portmanteau’ tests. 4 | #' 5 | #' @param x A numeric vector 6 | #' @param lag The number of lag autocorrelation coefficients to use in calculating the statistic 7 | #' @param dof Degrees of freedom of the fitted model (useful if x is a series of residuals). 8 | #' @param ... Unused. 9 | #' 10 | #' @return A vector of numeric features for the test's statistic and p-value. 11 | #' 12 | #' @seealso [stats::Box.test()] 13 | #' 14 | #' @examples 15 | #' ljung_box(rnorm(100)) 16 | #' 17 | #' @rdname portmanteau_tests 18 | #' @export 19 | ljung_box <- function(x, lag = 1, dof = 0, ...){ 20 | out <- stats::Box.test(x, lag = lag, type = "Ljung-Box", fitdf = dof) 21 | c(lb_stat = unname(out$statistic), lb_pvalue = out$p.value) 22 | } 23 | 24 | #' @rdname portmanteau_tests 25 | #' @examples 26 | #' box_pierce(rnorm(100)) 27 | #' @export 28 | box_pierce <- function(x, lag = 1, dof = 0, ...){ 29 | out <- stats::Box.test(x, lag = lag, type = "Box-Pierce", fitdf = dof) 30 | c(bp_stat = unname(out$statistic), bp_pvalue = out$p.value) 31 | } 32 | 33 | #' @rdname portmanteau_tests 34 | #' @export 35 | portmanteau_tests <- list(ljung_box, box_pierce) 36 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | add_class <- function(x, new_class){ 2 | `class<-`(x, union(new_class, class(x))) 3 | } 4 | 5 | `%||%` <- function(x, y) { 6 | if (is.null(x)) y else x 7 | } 8 | 9 | require_package <- function(pkg){ 10 | if(!requireNamespace(pkg, quietly = TRUE)){ 11 | abort( 12 | sprintf('The `%s` package must be installed to use this functionality. It can be installed with install.packages("%s")', pkg, pkg) 13 | ) 14 | } 15 | } 16 | 17 | `%empty%` <- function(x, y) { 18 | if (length(x) == 0) y else x 19 | } 20 | 21 | lag <- function(x, n){ 22 | if (n == 0) 23 | return(x) 24 | xlen <- length(x) 25 | n <- pmin(n, xlen) 26 | out <- c(rep(NA, n), x[seq_len(xlen - n)]) 27 | out 28 | } 29 | 30 | nest_keys <- function(.data, nm = "data"){ 31 | out <- unclass(key_data(.data)) 32 | key <- key_vars(.data) 33 | row_indices <- out[[length(out)]] 34 | out[[length(out)]] <- NULL 35 | col_nest <- -match(key, colnames(.data)) 36 | if(is_empty(col_nest)){ 37 | col_nest <- NULL 38 | } 39 | idx <- index_var(.data) 40 | idx2 <- index2_var(.data) 41 | ordered <- is_ordered(.data) 42 | regular <- is_regular(.data) 43 | out[[nm]] <- map(row_indices, function(x, i, j){ 44 | out <- if(is.null(j)) x[i,] else x[i,j] 45 | build_tsibble_meta( 46 | out, 47 | key_data = as_tibble(list(.rows = list(seq_along(i)))), 48 | index = idx, index2 = idx2, ordered = ordered, 49 | interval = if(length(i) > 1 && regular) interval_pull(out[[idx]]) else interval(.data) 50 | ) 51 | }, x = as_tibble(.data), j = col_nest) 52 | as_tibble(out) 53 | } 54 | 55 | unnest_tbl <- function(.data, tbl_col, .sep = NULL){ 56 | row_indices <- rep.int(seq_len(NROW(.data)), map_int(.data[[tbl_col[[1]]]], NROW)) 57 | 58 | nested_cols <- map(tbl_col, function(x){ 59 | lst_col <- .data[[x]] 60 | if(is.data.frame(lst_col[[1]])){ 61 | dplyr::bind_rows(!!!set_names(lst_col, rep(x, length(lst_col)))) 62 | } 63 | else{ 64 | list2(!!x := unlist(lst_col)) 65 | } 66 | }) 67 | 68 | if(!is.null(.sep)){ 69 | nested_cols <- map2( 70 | nested_cols, tbl_col, 71 | function(x, nm) set_names(x, paste(nm, colnames(x), sep = .sep)) 72 | ) 73 | } 74 | 75 | dplyr::bind_cols( 76 | .data[row_indices, setdiff(names(.data), tbl_col), drop = FALSE], # Parent cols 77 | !!!nested_cols # Nested cols 78 | ) 79 | } 80 | 81 | unnest_tsbl <- function(.data, tsbl_col, parent_key = NULL, interval = NULL){ 82 | tsbl <- .data[[tsbl_col]][[1L]] 83 | if (!is_tsibble(tsbl)) { 84 | abort("Unnested column is not a tsibble object.") 85 | } 86 | idx <- index(tsbl) 87 | idx_chr <- as_string(idx) 88 | key <- c(parent_key, key_vars(tsbl)) 89 | 90 | .data <- unnest_tbl(.data, tsbl_col) 91 | 92 | class(.data[[idx_chr]]) <- class(tsbl[[idx_chr]]) 93 | build_tsibble(.data, key = !!key, index = !!idx, 94 | index2 = !!index2(tsbl), ordered = is_ordered(tsbl), 95 | interval = interval%||%interval(tsbl)) 96 | } 97 | 98 | interval_to_period <- function(interval){ 99 | with(interval, lubridate::years(year) + 100 | lubridate::period(3*quarter + month, units = "month") + lubridate::weeks(week) + 101 | lubridate::days(day) + lubridate::hours(hour) + lubridate::minutes(minute) + 102 | lubridate::seconds(second) + lubridate::milliseconds(millisecond) + 103 | lubridate::microseconds(microsecond) + lubridate::nanoseconds(nanosecond)) 104 | } 105 | 106 | round_period <- function(period){ 107 | if(!lubridate::is.period(period)) return(period) 108 | if(!is.null(attr(period, "second"))){ 109 | attr(period, "minute") <- attr(period, "minute")%||%0 + attr(period, "second")%/%60 110 | attr(period, "second") <- attr(period, "second")%%60 111 | } 112 | 113 | if(!is.null(attr(period, "minute"))){ 114 | attr(period, "hour") <- attr(period, "hour")%||%0 + attr(period, "minute")%/%60 115 | attr(period, "minute") <- attr(period, "minute")%%60 116 | } 117 | 118 | if(!is.null(attr(period, "hour"))){ 119 | attr(period, "day") <- attr(period, "day") + attr(period, "hour")%/%24 120 | attr(period, "hour") <- attr(period, "hour")%%24 121 | } 122 | 123 | if(!is.null(attr(period, "month"))){ 124 | attr(period, "year") <- attr(period, "year") + attr(period, "month")%/%12 125 | attr(period, "month") <- attr(period, "month")%%12 126 | } 127 | period 128 | } 129 | 130 | floor_tsibble_date <- function(x, unit, ...){ 131 | UseMethod("floor_tsibble_date") 132 | } 133 | #' @export 134 | floor_tsibble_date.default <- function(x, unit, ...){ 135 | unit <- round_period(unit) 136 | if(unit == lubridate::weeks(1)){ 137 | unit <- "week" 138 | } 139 | lubridate::floor_date(x, unit, week_start = 1) 140 | } 141 | #' @export 142 | floor_tsibble_date.numeric <- function(x, unit, ...){ 143 | unit <- round_period(unit) 144 | unit <- if (unit@year != 0) unit@year else unit@.Data 145 | minx <- min(x) 146 | (x-minx)%/%unit * unit + minx 147 | } 148 | #' @export 149 | floor_tsibble_date.yearquarter <- function(x, unit, ...){ 150 | yearquarter(lubridate::floor_date(as_date(x), round_period(unit), ...)) 151 | } 152 | #' @export 153 | floor_tsibble_date.yearmonth <- function(x, unit, ...){ 154 | yearmonth(lubridate::floor_date(as_date(x), round_period(unit), ...)) 155 | } 156 | #' @export 157 | floor_tsibble_date.yearweek <- function(x, unit, ...){ 158 | unit <- round_period(unit) 159 | if ((unit@year > 0) && (unit@day > 0)) { 160 | abort("Specify a period of either years or weeks to plot, not both.") 161 | } 162 | x <- lubridate::as_date(x) 163 | mth <- lubridate::month(x) 164 | wk <- as.numeric(strftime(x, "%V")) 165 | year <- lubridate::year(x) - (mth == 1 & wk == 53) + (mth == 12 & wk == 1) 166 | 167 | if (unit@year > 0) { 168 | year <- year - (year - 1970)%%unit@year 169 | wk <- "01" 170 | x <- paste0(year, " W", wk) 171 | } 172 | else if (unit@day > 0) { 173 | unit <- unit@day 174 | if(unit%%7 > 0){ 175 | warn("A period with fractional weeks has been specified, rounding to the nearest week") 176 | unit <- round(unit/7)*7 177 | } 178 | x <- as.numeric(as_date(x)) + 3 179 | x <- structure((x %/% unit)*unit, class = "Date") - 3 180 | } 181 | yearweek(x) 182 | } 183 | 184 | time_origin <- function(x){ 185 | # Set origin at 1973-01-01 for weekday starting on Monday 186 | origin <- structure(94694400, class = c("POSIXct", "POSIXt"), tzone = "UTC") 187 | 188 | if (inherits(x, "yearweek")) tsibble::yearweek(origin) 189 | else if (inherits(x, "yearmonth")) tsibble::yearmonth(origin) 190 | else if (inherits(x, "yearquarter")) tsibble::yearquarter(origin) 191 | else if (is.numeric(x)) 0 192 | else if (inherits(x, "Date")) as.Date(origin) 193 | else origin 194 | } 195 | 196 | #' @importFrom lubridate years year month as_date 197 | time_offset_origin <- function(x, period, origin = time_origin(x)){ 198 | x_start <- floor_tsibble_date(x, period) 199 | 200 | if (inherits(x, "yearweek")) { 201 | tsibble::yearweek(as_date(origin) + (x - x_start)) 202 | } else if (inherits(x, "yearmonth")) { 203 | tsibble::yearmonth(as_date(origin) + years(year(x) - year(x_start)) + months(month(x) - month(x_start))) 204 | } else if (inherits(x, "yearquarter")) { 205 | tsibble::yearquarter(as_date(origin) + years(year(x) - year(x_start)) + months(month(x) - month(x_start))) 206 | } 207 | else origin + (x - x_start) 208 | } 209 | 210 | within_bounds <- function(x, lim) { 211 | if(!inherits(lim, class(x))) { 212 | lim <- vec_cast(lim, x) 213 | } 214 | x[x>=lim[1] & x<=lim[2]] 215 | } 216 | -------------------------------------------------------------------------------- /R/x11.R: -------------------------------------------------------------------------------- 1 | globalVariables("self") 2 | 3 | specials_X11 <- fabletools::new_specials( 4 | season = function(period = NULL){ 5 | period <- get_frequencies(period, self$data, .auto = "smallest") 6 | if(!(period %in% c(4, 12))){ 7 | abort("The X11 method only supports monthly (`period = 12`) and quarterly (`period = 4`) seasonal patterns") 8 | } 9 | period 10 | }, 11 | 12 | xreg = function(...){ 13 | abort("Exogenous regressors are not supported for X11 decompositions.") 14 | }, 15 | 16 | .required_specials = "season" 17 | ) 18 | 19 | train_X11 <- function(.data, formula, specials, type, ...){ 20 | require_package("seasonal") 21 | stopifnot(is_tsibble(.data)) 22 | 23 | if(length(specials$season) != 1){ 24 | abort("X11 only supports one seasonal period.") 25 | } 26 | period <- specials$season[[1]] 27 | y <- as.ts(.data, frequency = period) 28 | 29 | type <- switch(type, additive = "add", multiplicative = "mult") 30 | op <- switch(type, add = "+", mult = "*") 31 | 32 | fit <- seasonal::seas(y, x11 = "", x11.mode = type, 33 | transform.function = switch(type, add = "none", "log")) 34 | 35 | dcmp <- unclass(fit$data) 36 | 37 | dcmp <- .data %>% 38 | mutate( 39 | trend = dcmp[,"trend"], 40 | seasonal = dcmp[,"adjustfac"], 41 | irregular = dcmp[,"irregular"], 42 | season_adjust = !!call2(op, !!!syms(c("trend", "irregular"))) 43 | ) 44 | 45 | seasonalities <- list( 46 | seasonal = list(period = period, base = switch(op, `+` = 0, 1)) 47 | ) 48 | 49 | aliases <- list2( 50 | !!measured_vars(.data) := reduce(syms(c("trend", "seasonal", "irregular")), 51 | function(x,y) call2(op, x, y)), 52 | season_adjust = call2(op, sym("trend"), sym("irregular")) 53 | ) 54 | 55 | structure( 56 | list(decomposition = dcmp, 57 | response = measured_vars(.data), method = "X11", 58 | seasons = seasonalities, aliases = aliases 59 | ), 60 | class = "x11_decomposition" 61 | ) 62 | } 63 | 64 | #' @importFrom fabletools components as_dable 65 | #' @export 66 | components.x11_decomposition <- function(object, ...){ 67 | as_dable(object[["decomposition"]], response = !!sym(object[["response"]]), 68 | method = object[["method"]], seasons = object[["seasons"]], 69 | aliases = object[["aliases"]]) 70 | } 71 | 72 | #' @importFrom fabletools model_sum 73 | model_sum.x11_decomposition <- function(x){ 74 | "X11" 75 | } 76 | 77 | X11 <- function(formula, type = c("additive", "multiplicative"), ...){ 78 | lifecycle::deprecate_warn("0.2.0", "feasts::X11()", "feasts::X_13ARIMA_SEATS()", "You can specify the X-11 decomposition method by including x11() in the model formula of `X_13ARIMA_SEATS()`.") 79 | type <- match.arg(type) 80 | dcmp <- new_model_class("X11", 81 | train = train_X11, specials = specials_X11, 82 | check = all_tsbl_checks) 83 | new_model_definition(dcmp, !!enquo(formula), type = type, ...) 84 | } 85 | 86 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # nocov start 2 | .onLoad <- function(...) { 3 | fabletools::register_feature(feat_stl, c("stl", "trend", "seasonal", "decomposition")) 4 | fabletools::register_feature(feat_acf, c("acf", "autocorrelation")) 5 | fabletools::register_feature(feat_pacf, c("pacf", "autocorrelation")) 6 | fabletools::register_feature(feat_intermittent, c("intermittent")) 7 | fabletools::register_feature(guerrero, c("optimisation", "boxcox")) 8 | fabletools::register_feature(unitroot_kpss, c("test", "unitroot")) 9 | fabletools::register_feature(unitroot_pp, c("test", "unitroot")) 10 | fabletools::register_feature(unitroot_ndiffs, c("test", "unitroot")) 11 | fabletools::register_feature(unitroot_nsdiffs, c("test", "seasonal", "unitroot")) 12 | fabletools::register_feature(box_pierce, c("test", "portmanteau")) 13 | fabletools::register_feature(ljung_box, c("test", "portmanteau")) 14 | fabletools::register_feature(var_tiled_var, c("lumpiness", "tile")) 15 | fabletools::register_feature(var_tiled_mean, c("stability", "tile")) 16 | fabletools::register_feature(shift_level_max, c("roll", "slide")) 17 | fabletools::register_feature(shift_var_max, c("roll", "slide")) 18 | fabletools::register_feature(shift_kl_max, c("roll", "slide")) 19 | fabletools::register_feature(feat_spectral, c("spectral")) 20 | fabletools::register_feature(n_crossing_points, "count") 21 | fabletools::register_feature(longest_flat_spot, c("count", "rle")) 22 | fabletools::register_feature(coef_hurst, c("coefficients")) 23 | fabletools::register_feature(stat_arch_lm, c("test")) 24 | invisible() 25 | } 26 | 27 | register_s3_method <- function(pkg, generic, class, fun = NULL) { 28 | stopifnot(is.character(pkg), length(pkg) == 1) 29 | stopifnot(is.character(generic), length(generic) == 1) 30 | stopifnot(is.character(class), length(class) == 1) 31 | 32 | if (is.null(fun)) { 33 | fun <- get(paste0(generic, ".", class), envir = parent.frame()) 34 | } else { 35 | stopifnot(is.function(fun)) 36 | } 37 | 38 | if (pkg %in% loadedNamespaces()) { 39 | registerS3method(generic, class, fun, envir = asNamespace(pkg)) 40 | } 41 | 42 | # Always register hook in case package is later unloaded & reloaded 43 | setHook( 44 | packageEvent(pkg, "onLoad"), 45 | function(...) { 46 | registerS3method(generic, class, fun, envir = asNamespace(pkg)) 47 | } 48 | ) 49 | } 50 | # nocov end 51 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r setup, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | options(width = 100) 15 | ``` 16 | 17 | # feasts 18 | 19 | 20 | [![R build status](https://github.com/tidyverts/feasts/workflows/R-CMD-check/badge.svg)](https://github.com/tidyverts/feasts/actions?workflow=R-CMD-check) 21 | [![Coverage status](https://codecov.io/gh/tidyverts/feasts/branch/master/graph/badge.svg)](https://app.codecov.io/gh/tidyverts/feasts?branch=master) 22 | [![CRAN status](https://www.r-pkg.org/badges/version/feasts)](https://CRAN.R-project.org/package=feasts) 23 | [![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html) 24 | 25 | 26 | ## Overview 27 | 28 | feasts provides a collection of tools for the analysis of time series data. The package name is an acronym comprising of its key features: *Feature Extraction And Statistics for Time Series*. 29 | 30 | The package works with tidy temporal data provided by the [tsibble](https://github.com/tidyverts/tsibble) package to produce time series features, decompositions, statistical summaries and convenient visualisations. These features are useful in understanding the behaviour of time series data, and closely integrates with the tidy forecasting workflow used in the [fable](https://github.com/tidyverts/fable) package. 31 | 32 | ## Installation 33 | 34 | You could install the **stable** version from [CRAN](https://cran.r-project.org/package=feasts): 35 | 36 | ```{r, eval = FALSE} 37 | install.packages("feasts") 38 | ``` 39 | 40 | You can install the **development** version from 41 | [GitHub](https://github.com/tidyverts/feasts) with: 42 | 43 | ```{r gh-installation, eval = FALSE} 44 | # install.packages("remotes") 45 | remotes::install_github("tidyverts/feasts") 46 | ``` 47 | 48 | ## Usage 49 | 50 | ```{r pkgs, message = FALSE, warning = FALSE} 51 | library(feasts) 52 | library(tsibble) 53 | library(tsibbledata) 54 | library(dplyr) 55 | library(ggplot2) 56 | library(lubridate) 57 | ``` 58 | 59 | ### Graphics 60 | 61 | Visualisation is often the first step in understanding the patterns in time series data. The package uses [ggplot2](https://github.com/tidyverse/ggplot2) to produce customisable graphics to visualise time series patterns. 62 | 63 | ```{r graphics, fig.height=3} 64 | aus_production %>% gg_season(Beer) 65 | aus_production %>% gg_subseries(Beer) 66 | aus_production %>% filter(year(Quarter) > 1991) %>% gg_lag(Beer) 67 | aus_production %>% ACF(Beer) %>% autoplot() 68 | ``` 69 | 70 | ### Decompositions 71 | 72 | A common task in time series analysis is decomposing a time series into some simpler components. The feasts package supports two common time series decomposition methods: 73 | 74 | * Classical decomposition 75 | * STL decomposition 76 | 77 | 81 | 82 | ```{r dcmp} 83 | dcmp <- aus_production %>% 84 | model(STL(Beer ~ season(window = Inf))) 85 | components(dcmp) 86 | ``` 87 | ```{r dcmp-plot} 88 | components(dcmp) %>% autoplot() 89 | ``` 90 | 91 | ### Feature extraction and statistics 92 | 93 | Extract features and statistics across a large collection of time series to identify unusual/extreme time series, or find clusters of similar behaviour. 94 | 95 | ```{r features} 96 | aus_retail %>% 97 | features(Turnover, feat_stl) 98 | ``` 99 | 100 | This allows you to visualise the behaviour of many time series (where the plotting methods above would show too much information). 101 | 102 | ```{r features-plot} 103 | aus_retail %>% 104 | features(Turnover, feat_stl) %>% 105 | ggplot(aes(x = trend_strength, y = seasonal_strength_year)) + 106 | geom_point() + 107 | facet_wrap(vars(State)) 108 | ``` 109 | 110 | Most of Australian's retail industries are highly trended and seasonal for all states. 111 | 112 | It's also easy to extract the most (and least) seasonal time series. 113 | 114 | ```{r extreme} 115 | extreme_seasonalities <- aus_retail %>% 116 | features(Turnover, feat_stl) %>% 117 | filter(seasonal_strength_year %in% range(seasonal_strength_year)) 118 | aus_retail %>% 119 | right_join(extreme_seasonalities, by = c("State", "Industry")) %>% 120 | ggplot(aes(x = Month, y = Turnover)) + 121 | geom_line() + 122 | facet_grid(vars(State, Industry, scales::percent(seasonal_strength_year)), 123 | scales = "free_y") 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # feasts 5 | 6 | 7 | 8 | [![R build 9 | status](https://github.com/tidyverts/feasts/workflows/R-CMD-check/badge.svg)](https://github.com/tidyverts/feasts/actions?workflow=R-CMD-check) 10 | [![Coverage 11 | status](https://codecov.io/gh/tidyverts/feasts/branch/master/graph/badge.svg)](https://app.codecov.io/gh/tidyverts/feasts?branch=master) 12 | [![CRAN 13 | status](https://www.r-pkg.org/badges/version/feasts)](https://CRAN.R-project.org/package=feasts) 14 | [![Lifecycle: 15 | maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html) 16 | 17 | 18 | ## Overview 19 | 20 | feasts provides a collection of tools for the analysis of time series 21 | data. The package name is an acronym comprising of its key features: 22 | *Feature Extraction And Statistics for Time Series*. 23 | 24 | The package works with tidy temporal data provided by the 25 | [tsibble](https://github.com/tidyverts/tsibble) package to produce time 26 | series features, decompositions, statistical summaries and convenient 27 | visualisations. These features are useful in understanding the behaviour 28 | of time series data, and closely integrates with the tidy forecasting 29 | workflow used in the [fable](https://github.com/tidyverts/fable) 30 | package. 31 | 32 | ## Installation 33 | 34 | You could install the **stable** version from 35 | [CRAN](https://cran.r-project.org/package=feasts): 36 | 37 | ``` r 38 | install.packages("feasts") 39 | ``` 40 | 41 | You can install the **development** version from 42 | [GitHub](https://github.com/tidyverts/feasts) with: 43 | 44 | ``` r 45 | # install.packages("remotes") 46 | remotes::install_github("tidyverts/feasts") 47 | ``` 48 | 49 | ## Usage 50 | 51 | ``` r 52 | library(feasts) 53 | library(tsibble) 54 | library(tsibbledata) 55 | library(dplyr) 56 | library(ggplot2) 57 | library(lubridate) 58 | ``` 59 | 60 | ### Graphics 61 | 62 | Visualisation is often the first step in understanding the patterns in 63 | time series data. The package uses 64 | [ggplot2](https://github.com/tidyverse/ggplot2) to produce customisable 65 | graphics to visualise time series patterns. 66 | 67 | ``` r 68 | aus_production %>% gg_season(Beer) 69 | ``` 70 | 71 | 72 | 73 | ``` r 74 | aus_production %>% gg_subseries(Beer) 75 | ``` 76 | 77 | 78 | 79 | ``` r 80 | aus_production %>% filter(year(Quarter) > 1991) %>% gg_lag(Beer) 81 | ``` 82 | 83 | 84 | 85 | ``` r 86 | aus_production %>% ACF(Beer) %>% autoplot() 87 | ``` 88 | 89 | 90 | 91 | ### Decompositions 92 | 93 | A common task in time series analysis is decomposing a time series into 94 | some simpler components. The feasts package supports two common time 95 | series decomposition methods: 96 | 97 | - Classical decomposition 98 | - STL decomposition 99 | 100 | 104 | 105 | ``` r 106 | dcmp <- aus_production %>% 107 | model(STL(Beer ~ season(window = Inf))) 108 | components(dcmp) 109 | #> # A dable: 218 x 7 [1Q] 110 | #> # Key: .model [1] 111 | #> # : Beer = trend + season_year + remainder 112 | #> .model Quarter Beer trend season_year remainder season_adjust 113 | #> 114 | #> 1 STL(Beer ~ season(window = Inf)) 1956 Q1 284 272. 2.14 10.1 282. 115 | #> 2 STL(Beer ~ season(window = Inf)) 1956 Q2 213 264. -42.6 -8.56 256. 116 | #> 3 STL(Beer ~ season(window = Inf)) 1956 Q3 227 258. -28.5 -2.34 255. 117 | #> 4 STL(Beer ~ season(window = Inf)) 1956 Q4 308 253. 69.0 -14.4 239. 118 | #> 5 STL(Beer ~ season(window = Inf)) 1957 Q1 262 257. 2.14 2.55 260. 119 | #> 6 STL(Beer ~ season(window = Inf)) 1957 Q2 228 261. -42.6 9.47 271. 120 | #> 7 STL(Beer ~ season(window = Inf)) 1957 Q3 236 263. -28.5 1.80 264. 121 | #> 8 STL(Beer ~ season(window = Inf)) 1957 Q4 320 264. 69.0 -12.7 251. 122 | #> 9 STL(Beer ~ season(window = Inf)) 1958 Q1 272 266. 2.14 4.32 270. 123 | #> 10 STL(Beer ~ season(window = Inf)) 1958 Q2 233 266. -42.6 9.72 276. 124 | #> # i 208 more rows 125 | ``` 126 | 127 | ``` r 128 | components(dcmp) %>% autoplot() 129 | ``` 130 | 131 | 132 | 133 | ### Feature extraction and statistics 134 | 135 | Extract features and statistics across a large collection of time series 136 | to identify unusual/extreme time series, or find clusters of similar 137 | behaviour. 138 | 139 | ``` r 140 | aus_retail %>% 141 | features(Turnover, feat_stl) 142 | #> # A tibble: 152 x 11 143 | #> State Industry trend_strength seasonal_strength_year seasonal_peak_year seasonal_trough_year 144 | #> 145 | #> 1 Australia~ Cafes, ~ 0.989 0.562 0 10 146 | #> 2 Australia~ Cafes, ~ 0.993 0.629 0 10 147 | #> 3 Australia~ Clothin~ 0.991 0.923 9 11 148 | #> 4 Australia~ Clothin~ 0.993 0.957 9 11 149 | #> 5 Australia~ Departm~ 0.977 0.980 9 11 150 | #> 6 Australia~ Electri~ 0.992 0.933 9 11 151 | #> 7 Australia~ Food re~ 0.999 0.890 9 11 152 | #> 8 Australia~ Footwea~ 0.982 0.944 9 11 153 | #> 9 Australia~ Furnitu~ 0.981 0.687 9 1 154 | #> 10 Australia~ Hardwar~ 0.992 0.900 9 4 155 | #> # i 142 more rows 156 | #> # i 5 more variables: spikiness , linearity , curvature , stl_e_acf1 , 157 | #> # stl_e_acf10 158 | ``` 159 | 160 | This allows you to visualise the behaviour of many time series (where 161 | the plotting methods above would show too much information). 162 | 163 | ``` r 164 | aus_retail %>% 165 | features(Turnover, feat_stl) %>% 166 | ggplot(aes(x = trend_strength, y = seasonal_strength_year)) + 167 | geom_point() + 168 | facet_wrap(vars(State)) 169 | ``` 170 | 171 | 172 | 173 | Most of Australian’s retail industries are highly trended and seasonal 174 | for all states. 175 | 176 | It’s also easy to extract the most (and least) seasonal time series. 177 | 178 | ``` r 179 | extreme_seasonalities <- aus_retail %>% 180 | features(Turnover, feat_stl) %>% 181 | filter(seasonal_strength_year %in% range(seasonal_strength_year)) 182 | aus_retail %>% 183 | right_join(extreme_seasonalities, by = c("State", "Industry")) %>% 184 | ggplot(aes(x = Month, y = Turnover)) + 185 | geom_line() + 186 | facet_grid(vars(State, Industry, scales::percent(seasonal_strength_year)), 187 | scales = "free_y") 188 | ``` 189 | 190 | 191 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://feasts.tidyverts.org 2 | 3 | template: 4 | params: 5 | bootswatch: cosmo 6 | includes: 7 | in_header: | 8 | 9 | 10 | development: 11 | mode: auto 12 | 13 | authors: 14 | Mitchell O'Hara-Wild: 15 | href: https://mitchelloharawild.com 16 | Rob Hyndman: 17 | href: http://robjhyndman.com 18 | Earo Wang: 19 | href: https://earo.me 20 | 21 | navbar: 22 | type: default 23 | left: 24 | - text: Introduction 25 | href: articles/feasts.html 26 | - text: Reference 27 | href: reference/index.html 28 | - text: Changelog 29 | href: news/index.html 30 | # - text: Vignettes 31 | # menu: 32 | # - text: Introduction to feasts 33 | # href: articles/feasts.html 34 | - text: News 35 | menu: 36 | - text: "Release notes" 37 | - text: "Version 0.1.0" 38 | href: https://www.mitchelloharawild.com/blog/feasts/ 39 | - text: "Change log" 40 | href: news/index.html 41 | right: 42 | - icon: fa-github fa-lg 43 | href: https://github.com/tidyverts/feasts 44 | 45 | reference: 46 | - title: Overview 47 | contents: 48 | - feasts-package 49 | - title: Graphics 50 | desc: > 51 | Visualisation is often the first step in understanding the patterns in time series data. 52 | The package uses [ggplot2](https://ggplot2.tidyverse.org/) to produce customisable graphics to visualise time series patterns. 53 | contents: 54 | - autoplot.tbl_cf 55 | - gg_season 56 | - gg_subseries 57 | - gg_tsdisplay 58 | - gg_tsresiduals 59 | - gg_lag 60 | - gg_arma 61 | - gg_irf 62 | 63 | - title: Decompositions 64 | desc: > 65 | Useful for decomposing a time series into some simpler structural components. 66 | contents: 67 | - classical_decomposition 68 | - STL 69 | - generate.stl_decomposition 70 | - X_13ARIMA_SEATS 71 | 72 | - title: Autocorrelation analysis 73 | desc: > 74 | Identify autocorrelations in the data. 75 | contents: 76 | - ACF 77 | - PACF 78 | - CCF 79 | - feat_acf 80 | - feat_pacf 81 | 82 | - title: Unit root tests 83 | desc: > 84 | Unit root tests for use with [`fabletools::features()`](https://fabletools.tidyverts.org/reference/features.html). 85 | contents: 86 | - unitroot_kpss 87 | - unitroot_pp 88 | - unitroot_ndiffs 89 | - unitroot_nsdiffs 90 | 91 | - title: Portmanteau tests 92 | desc: > 93 | Statistical tests for examining the null hypothesis of independence in a given time series. 94 | contents: 95 | - portmanteau_tests 96 | - box_pierce 97 | - ljung_box 98 | 99 | - title: Cointegration tests 100 | desc: > 101 | Statistical tests for investigating cointegration between time series. 102 | contents: 103 | - cointegration_johansen 104 | - cointegration_phillips_ouliaris 105 | 106 | - title: Tiling window features 107 | desc: > 108 | Computes feature of a time series based on tiled (non-overlapping) windows. 109 | contents: 110 | - var_tiled_var 111 | - var_tiled_mean 112 | 113 | - title: Sliding window features 114 | desc: > 115 | Computes feature of a time series based on sliding (overlapping) windows. 116 | contents: 117 | - shift_level_max 118 | - shift_var_max 119 | - shift_kl_max 120 | 121 | - title: Other features 122 | desc: > 123 | Uncategorised features 124 | contents: 125 | - feat_stl 126 | - feat_spectral 127 | - feat_intermittent 128 | - stat_arch_lm 129 | - n_crossing_points 130 | - n_flat_spots 131 | - coef_hurst 132 | - guerrero 133 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | patch: 10 | default: 11 | target: auto 12 | threshold: 1% 13 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Test environments 2 | * local ubuntu 20.04 install, R 4.1.2 3 | * ubuntu-latest (on GitHub actions), R 4.0.0, R 3.6.3, R 3.5.3 4 | * macOS-latest (on GitHub actions), R-devel, R 4.0.0 5 | * windows-latest (on GitHub actions), R 4.0.0 6 | * win-builder, R-devel, R-release, R-oldrelease 7 | 8 | ## R CMD check results 9 | 10 | 0 errors | 0 warnings | 0 notes 11 | 12 | ## Revdep checks 13 | 14 | All reverse dependencies have been checked, none have changed to worse. 15 | -------------------------------------------------------------------------------- /feasts.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 17a3c648-238f-4572-9141-f25cb1060ddd 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: XeLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /hex/feasts.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/hex/feasts.ai -------------------------------------------------------------------------------- /hex/feasts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/hex/feasts.png -------------------------------------------------------------------------------- /hex/feasts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 29 | 31 | 36 | 41 | 42 | 46 | 47 | 48 | 51 | 55 | 60 | 65 | 68 | 73 | 74 | 75 | 77 | 79 | 81 | 83 | 84 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 98 | 101 | 103 | 106 | 108 | 111 | 113 | 116 | 119 | 121 | 123 | 126 | 127 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /hex/feasts_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/hex/feasts_icon.png -------------------------------------------------------------------------------- /man/ACF.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/acf.R 3 | \name{ACF} 4 | \alias{ACF} 5 | \alias{PACF} 6 | \alias{CCF} 7 | \title{(Partial) Autocorrelation and Cross-Correlation Function Estimation} 8 | \usage{ 9 | ACF( 10 | .data, 11 | y, 12 | ..., 13 | lag_max = NULL, 14 | type = c("correlation", "covariance", "partial"), 15 | na.action = na.contiguous, 16 | demean = TRUE, 17 | tapered = FALSE 18 | ) 19 | 20 | PACF(.data, y, ..., lag_max = NULL, na.action = na.contiguous, tapered = FALSE) 21 | 22 | CCF( 23 | .data, 24 | y, 25 | x, 26 | ..., 27 | lag_max = NULL, 28 | type = c("correlation", "covariance"), 29 | na.action = na.contiguous 30 | ) 31 | } 32 | \arguments{ 33 | \item{.data}{A tsibble} 34 | 35 | \item{...}{The column(s) from the tsibble used to compute the ACF, PACF or CCF.} 36 | 37 | \item{lag_max}{maximum lag at which to calculate the acf. Default is 10*log10(N/m) 38 | where N is the number of observations and m the number of series. Will be 39 | automatically limited to one less than the number of observations in the series.} 40 | 41 | \item{type}{character string giving the type of ACF to be computed. Allowed values are \code{"correlation"} (the default), \code{"covariance"} or \code{"partial"}.} 42 | 43 | \item{na.action}{function to be called to handle missing 44 | values. \code{na.pass} can be used.} 45 | 46 | \item{demean}{logical. Should the covariances be about the sample 47 | means?} 48 | 49 | \item{tapered}{Produces banded and tapered estimates of the (partial) autocorrelation.} 50 | 51 | \item{x, y}{a univariate or multivariate (not \code{ccf}) numeric time 52 | series object or a numeric vector or matrix, or an \code{"acf"} object.} 53 | } 54 | \value{ 55 | The \code{ACF}, \code{PACF} and \code{CCF} functions return objects 56 | of class "tbl_cf", which is a tsibble containing the correlations computed. 57 | } 58 | \description{ 59 | The function \code{ACF} computes an estimate of the autocorrelation function 60 | of a (possibly multivariate) tsibble. Function \code{PACF} computes an estimate 61 | of the partial autocorrelation function of a (possibly multivariate) tsibble. 62 | Function \code{CCF} computes the cross-correlation or cross-covariance of two columns 63 | from a tsibble. 64 | } 65 | \details{ 66 | The functions improve the \code{\link[stats:acf]{stats::acf()}}, \code{\link[stats:acf]{stats::pacf()}} and 67 | \code{\link[stats:acf]{stats::ccf()}} functions. The main differences are that \code{ACF} does not plot 68 | the exact correlation at lag 0 when \code{type=="correlation"} and 69 | the horizontal axes show lags in time units rather than seasonal units. 70 | 71 | The resulting tables from these functions can also be plotted using 72 | \code{\link[=autoplot.tbl_cf]{autoplot.tbl_cf()}}. 73 | } 74 | \examples{ 75 | library(tsibble) 76 | library(tsibbledata) 77 | library(dplyr) 78 | 79 | vic_elec \%>\% ACF(Temperature) 80 | 81 | vic_elec \%>\% ACF(Temperature) \%>\% autoplot() 82 | 83 | vic_elec \%>\% PACF(Temperature) 84 | 85 | vic_elec \%>\% PACF(Temperature) \%>\% autoplot() 86 | 87 | global_economy \%>\% 88 | filter(Country == "Australia") \%>\% 89 | CCF(GDP, Population) 90 | 91 | global_economy \%>\% 92 | filter(Country == "Australia") \%>\% 93 | CCF(GDP, Population) \%>\% 94 | autoplot() 95 | 96 | } 97 | \references{ 98 | Hyndman, R.J. (2015). Discussion of "High-dimensional 99 | autocovariance matrices and optimal linear prediction". \emph{Electronic 100 | Journal of Statistics}, 9, 792-796. 101 | 102 | McMurry, T. L., & Politis, D. N. (2010). Banded and tapered estimates for 103 | autocovariance matrices and the linear process bootstrap. \emph{Journal of 104 | Time Series Analysis}, 31(6), 471-482. 105 | } 106 | \seealso{ 107 | \code{\link[stats:acf]{stats::acf()}}, \code{\link[stats:acf]{stats::pacf()}}, \code{\link[stats:acf]{stats::ccf()}} 108 | } 109 | \author{ 110 | Mitchell O'Hara-Wild and Rob J Hyndman 111 | } 112 | -------------------------------------------------------------------------------- /man/STL.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stl.R 3 | \name{STL} 4 | \alias{STL} 5 | \title{Multiple seasonal decomposition by Loess} 6 | \usage{ 7 | STL(formula, iterations = 2, ...) 8 | } 9 | \arguments{ 10 | \item{formula}{Decomposition specification (see "Specials" section).} 11 | 12 | \item{iterations}{Number of iterations to use to refine the seasonal component.} 13 | 14 | \item{...}{Other arguments passed to \code{\link[stats:stl]{stats::stl()}}.} 15 | } 16 | \value{ 17 | A \code{\link[fabletools:dable]{fabletools::dable()}} containing the decomposed trend, seasonality 18 | and remainder from the STL decomposition. 19 | } 20 | \description{ 21 | Decompose a time series into seasonal, trend and remainder components. 22 | Seasonal components are estimated iteratively using STL. Multiple seasonal periods are 23 | allowed. The trend component is computed for the last iteration of STL. 24 | Non-seasonal time series are decomposed into trend and remainder only. 25 | In this case, \code{\link[stats]{supsmu}} is used to estimate the trend. 26 | Optionally, the time series may be Box-Cox transformed before decomposition. 27 | Unlike \code{\link[stats]{stl}}, \code{mstl} is completely automated. 28 | } 29 | \section{Specials}{ 30 | 31 | 32 | \subsection{trend}{ 33 | The \code{trend} special is used to specify the trend extraction parameters. 34 | \preformatted{ 35 | trend(window, degree, jump) 36 | } 37 | 38 | \tabular{ll}{ 39 | \code{window} \tab The span (in lags) of the loess window, which should be odd. If NULL, the default, nextodd(ceiling((1.5*period) / (1-(1.5/s.window)))), is taken.\cr 40 | \code{degree} \tab The degree of locally-fitted polynomial. Should be zero or one. \cr 41 | \code{jump} \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every \code{jump}th value. 42 | } 43 | } 44 | 45 | \subsection{season}{ 46 | The \code{season} special is used to specify the season extraction parameters. 47 | \preformatted{ 48 | season(period = NULL, window = NULL, degree, jump) 49 | } 50 | 51 | \tabular{ll}{ 52 | \code{period} \tab The periodic nature of the seasonality. This can be either a number indicating the number of observations in each seasonal period, or text to indicate the duration of the seasonal window (for example, annual seasonality would be "1 year").\cr 53 | \code{window} \tab The span (in lags) of the loess window, which should be odd. If the \code{window} is set to \code{"periodic"} or \code{Inf}, the seasonal pattern will be fixed. The window size should be odd and at least 7, according to Cleveland et al. The default (NULL) will choose an appropriate default, for a dataset with one seasonal pattern this would be 11, the second larger seasonal window would be 15, then 19, 23, ... onwards.\cr 54 | \code{degree} \tab The degree of locally-fitted polynomial. Should be zero or one. \cr 55 | \code{jump} \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every \code{jump}th value. 56 | } 57 | } 58 | 59 | \subsection{lowpass}{ 60 | The \code{lowpass} special is used to specify the low-pass filter parameters. 61 | \preformatted{ 62 | lowpass(window, degree, jump) 63 | } 64 | 65 | \tabular{ll}{ 66 | \code{window} \tab The span (in lags) of the loess window of the low-pass filter used for each subseries. Defaults to the smallest odd integer greater than or equal to the seasonal \code{period} which is recommended since it prevents competition between the trend and seasonal components. If not an odd integer its given value is increased to the next odd one. \cr 67 | \code{degree} \tab The degree of locally-fitted polynomial. Must be zero or one. \cr 68 | \code{jump} \tab Integers at least one to increase speed of the respective smoother. Linear interpolation happens between every \code{jump}th value. 69 | } 70 | } 71 | } 72 | 73 | \examples{ 74 | as_tsibble(USAccDeaths) \%>\% 75 | model(STL(value ~ trend(window = 10))) \%>\% 76 | components() 77 | 78 | } 79 | \references{ 80 | R. B. Cleveland, W. S. Cleveland, J.E. McRae, and I. Terpenning (1990) STL: A Seasonal-Trend Decomposition Procedure Based on Loess. Journal of Official Statistics, 6, 3–73. 81 | } 82 | \seealso{ 83 | \code{\link[stats]{stl}}, \code{\link[stats]{supsmu}} 84 | } 85 | -------------------------------------------------------------------------------- /man/X_13ARIMA_SEATS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/x13arimaseats.R 3 | \name{X_13ARIMA_SEATS} 4 | \alias{X_13ARIMA_SEATS} 5 | \title{X-13ARIMA-SEATS Seasonal Adjustment} 6 | \usage{ 7 | X_13ARIMA_SEATS( 8 | formula, 9 | ..., 10 | na.action = seasonal::na.x13, 11 | defaults = c("seasonal", "none") 12 | ) 13 | } 14 | \arguments{ 15 | \item{formula}{Decomposition specification.} 16 | 17 | \item{...}{Other arguments passed to \code{\link[seasonal:seas]{seasonal::seas()}}.} 18 | 19 | \item{na.action}{a function which indicates what should happen when the data 20 | contain NAs. \code{na.omit} (default), \code{na.exclude} or \code{na.fail}. 21 | If \code{na.action = na.x13}, NA handling is done by X-13, i.e. NA values 22 | are substituted by -99999.} 23 | 24 | \item{defaults}{If defaults="seasonal", the default options of 25 | \code{\link[seasonal:seas]{seasonal::seas()}} will be used, which should work well in most 26 | circumstances. Setting defaults="none" gives an empty model specification, 27 | which can be added to in the model formula.} 28 | } 29 | \description{ 30 | X-13ARIMA-SEATS is a seasonal adjustment program developed and maintained by 31 | the U.S. Census Bureau. 32 | } 33 | \details{ 34 | The SEATS decomposition method stands for "Seasonal 35 | Extraction in ARIMA Time Series", and is the default method for seasonally 36 | adjusting the data. This decomposition method can extract seasonality from 37 | data with seasonal periods of 2 (biannual), 4 (quarterly), 6 (bimonthly), 38 | and 12 (monthly). This method is specified using the \code{seats()} function in 39 | the model formula. 40 | 41 | Alternatively, the seasonal adjustment can be done using an enhanced X-11 42 | decomposition method. The X-11 method uses weighted averages over a moving 43 | window of the time series. This is used in combination with the RegARIMA 44 | model to prepare the data for decomposition. To use the X-11 decomposition 45 | method, the \code{x11()} function can be used in the model formula. 46 | } 47 | \section{Specials}{ 48 | 49 | 50 | The specials of the X-13ARIMA-SEATS model closely follow the individual 51 | specification options of the original function. Refer to 52 | \href{https://www2.census.gov/software/x-13arima-seats/x13as/windows/documentation/docx13as.pdf#chapter.7}{Chapter 7 of the X-13ARIMA-SEATS Reference Manual} 53 | for full details of the arguments. 54 | 55 | The available specials for this model are: 56 | 57 | #' \subsection{arima}{ 58 | The \code{arima} special is used to specify the ARIMA part of the regARIMA model. 59 | This defines a pure ARIMA model if the \code{regression()} special absent and if 60 | no exogenous regressors are specified. The lags of the ARIMA model can be 61 | specified in the \code{model} argument, potentially along with \code{ar} and \code{ma} 62 | coefficients. 63 | 64 | \preformatted{ 65 | arima(...) 66 | } 67 | 68 | \tabular{ll}{ 69 | \code{...} \tab Arguments described in the reference manual linked below. 70 | } 71 | } 72 | \subsection{automdl}{ 73 | The \code{automdl} special is used to specify the ARIMA part of the regARIMA 74 | model will be sought using an automatic model selection procedure 75 | derived from the one used by TRAMO (see Gomez and Maravall (2001a)). The 76 | maximum order of lags and differencing can be specified using \code{maxorder} and 77 | \code{maxdiff} arguments. Models containing mixtures of AR and MA components can 78 | be allowed or disallowed using the \code{mixed} argument. 79 | 80 | \preformatted{ 81 | automdl(...) 82 | } 83 | 84 | \tabular{ll}{ 85 | \code{...} \tab Arguments described in the reference manual linked below. 86 | } 87 | } 88 | \subsection{check}{ 89 | The \code{check} special is used to produce statistics for diagnostic checking of 90 | residuals from the estimated model. The computed statistics include ACF and 91 | PACF of residuals, along with some statistical tests. These calculations are 92 | included in the model object, but difficult to access. It is recommended that 93 | these checks are done in R after estimating the model, and that this special 94 | is not used. 95 | 96 | \preformatted{ 97 | check(...) 98 | } 99 | 100 | \tabular{ll}{ 101 | \code{...} \tab Arguments described in the reference manual linked below. 102 | } 103 | } 104 | 105 | \subsection{estimate}{ 106 | The \code{estimate} special is used to specify optimisation parameters and 107 | estimation options for the regARIMA model specified by the \code{regression()} 108 | and \code{arima()} specials. Among other options, the tolerance can be set with 109 | \code{tol}, and maximum iterations can be set with \code{maxiter}. 110 | 111 | \preformatted{ 112 | estimate(...) 113 | } 114 | 115 | \tabular{ll}{ 116 | \code{...} \tab Arguments described in the reference manual linked below. 117 | } 118 | } 119 | \subsection{force}{ 120 | The \code{force} is an optional special for invoking options that allow users to 121 | force yearly totals of the seasonally adjusted series to equal those of the 122 | original series for convenience. 123 | 124 | \preformatted{ 125 | force(...) 126 | } 127 | 128 | \tabular{ll}{ 129 | \code{...} \tab Arguments described in the reference manual linked below. 130 | } 131 | } 132 | \subsection{forecast}{ 133 | The \code{forecast} special is used to specify options for forecasting and/or 134 | backcasting the time series using the estimated model. This process is used 135 | to enhance the decomposition procedure, especially its performance at the 136 | start and end of the series. The number of forecasts to produce is specified 137 | in the \code{maxlead} argument, and the number of backcasts in the \code{maxback} 138 | argument. 139 | 140 | \preformatted{ 141 | forecast(...) 142 | } 143 | 144 | \tabular{ll}{ 145 | \code{...} \tab Arguments described in the reference manual linked below. 146 | } 147 | } 148 | \subsection{history}{ 149 | The \code{history} special is an optional special for requesting a sequence of 150 | runs from a sequence of truncated versions of the time series. Using this 151 | special can substantially slow down the program. 152 | 153 | \preformatted{ 154 | history(...) 155 | } 156 | 157 | \tabular{ll}{ 158 | \code{...} \tab Arguments described in the reference manual linked below. 159 | } 160 | } 161 | \subsection{metadata}{ 162 | The \code{metadata} special is used to insert metadata into the diagnostic summary 163 | file. This is typically not needed when interacting with the program via R. 164 | 165 | \preformatted{ 166 | metadata(...) 167 | } 168 | 169 | \tabular{ll}{ 170 | \code{...} \tab Arguments described in the reference manual linked below. 171 | } 172 | } 173 | \subsection{identify}{ 174 | The \code{identify} special is used to produce tables and line printer plots of 175 | sample ACFs and PACFs for identifying the ARIMA part of a regARIMA model. 176 | 177 | \preformatted{ 178 | identify(...) 179 | } 180 | 181 | \tabular{ll}{ 182 | \code{...} \tab Arguments described in the reference manual linked below. 183 | } 184 | } 185 | \subsection{outlier}{ 186 | The \code{outlier} special is used to perform automatic detection of additive 187 | (point) outliers, temporary change outliers, level shifts, or any combination 188 | of the three using the specified model. The \code{seasonal::seas()} defaults used 189 | when \code{defaults="seasonal"} will include the default automatic detection of 190 | outliers. 191 | 192 | \preformatted{ 193 | outlier(...) 194 | } 195 | 196 | \tabular{ll}{ 197 | \code{...} \tab Arguments described in the reference manual linked below. 198 | } 199 | } 200 | \subsection{pickmdl}{ 201 | The \code{pickmdl} special is used to specify the ARIMA part of the regARIMA 202 | model will be sought using an automatic model selectionprocedure 203 | similar to the one used by X-11-ARIMA/88 (see Dagum 1988). 204 | 205 | \preformatted{ 206 | pickmdl(...) 207 | } 208 | 209 | \tabular{ll}{ 210 | \code{...} \tab Arguments described in the reference manual linked below. 211 | } 212 | } 213 | \subsection{regression}{ 214 | 215 | The \code{regression} special is used to specify including regression variables 216 | in a regARIMA model, or for specifying regression variables whose 217 | effects are to be removed by the \code{identify()} special to aid ARIMA model 218 | identification. Any exogenous regressors specified in the model formula will 219 | be passed into this specification via the \code{user} and \code{data} arguments. The 220 | \code{\link[seasonal:seas]{seasonal::seas()}} defaults used when \code{defaults="seasonal"} will set 221 | \code{aictest = c("td", "easter")}, indicating that trading days and Easter 222 | effects will be included conditional on AIC-based selection methods. 223 | 224 | \preformatted{ 225 | regression(...) 226 | } 227 | 228 | \tabular{ll}{ 229 | \code{...} \tab Arguments described in the reference manual linked below. 230 | } 231 | } 232 | \subsection{seats}{ 233 | The \code{seats} special is optionally used to invoke the production of model 234 | based signal extraction using SEATS, a seasonal adjustment program developed 235 | by Victor Gomez and Agustin Maravall at the Bank of Spain. 236 | 237 | \preformatted{ 238 | seats(...) 239 | } 240 | 241 | \tabular{ll}{ 242 | \code{...} \tab Arguments described in the reference manual linked below. 243 | } 244 | } 245 | \subsection{slidingspans}{ 246 | The optional \code{slidingspans} special is to provide sliding spans stability 247 | analysis on the model. These compare different features of seasonal 248 | adjustment output from overlapping subspans of the time series data. 249 | 250 | \preformatted{ 251 | slidingspans(...) 252 | } 253 | 254 | \tabular{ll}{ 255 | \code{...} \tab Arguments described in the reference manual linked below. 256 | } 257 | } 258 | \subsection{spectrum}{ 259 | The optional \code{spectrum} special is used to provide a choice between two 260 | spectrum diagnostics to detect seasonality or trading day effects in 261 | monthly series. 262 | 263 | \preformatted{ 264 | spectrum(...) 265 | } 266 | 267 | \tabular{ll}{ 268 | \code{...} \tab Arguments described in the reference manual linked below. 269 | } 270 | } 271 | \subsection{transform}{ 272 | The \code{transform} special is used to transform or adjust the series prior to 273 | estimating a regARIMA model. This is comparable to transforming the response 274 | on the formula's left hand side, but offers X-13ARIMA-SEATS specific 275 | adjustment options. 276 | 277 | \preformatted{ 278 | transform(...) 279 | } 280 | 281 | \tabular{ll}{ 282 | \code{...} \tab Arguments described in the reference manual linked below. 283 | } 284 | } 285 | \subsection{x11}{ 286 | The optional \code{x11} special is used to invoke seasonal adjustment by 287 | an enhanced version of the methodology of the Census Bureau X-11 and X-11Q 288 | programs. The user can control the type of seasonal adjustment decomposition 289 | calculated (\code{mode}), the seasonal and trend moving averages used 290 | (\code{seasonalma} and \code{trendma}), and the type of extreme value adjustment 291 | performed during seasonal adjustment (\code{sigmalim}). 292 | 293 | \preformatted{ 294 | x11(...) 295 | } 296 | 297 | \tabular{ll}{ 298 | \code{...} \tab Arguments described in the reference manual linked below. 299 | } 300 | } 301 | \subsection{x11regression}{ 302 | The \code{x11regression} special is used in conjunction with the \code{x11()} special 303 | for series without missing observations. This special estimates calendar 304 | effects by regression modeling of the irregular component with predefined or 305 | user-defined regressors. Any exogenous regressors specified in the model 306 | formula will be passed into this specification via the \code{user} and \code{data} 307 | arguments. 308 | 309 | \preformatted{ 310 | x11regression(...) 311 | } 312 | 313 | \tabular{ll}{ 314 | \code{...} \tab Arguments described in the reference manual linked below. 315 | } 316 | } 317 | } 318 | 319 | \examples{ 320 | 321 | \donttest{ 322 | fit <- tsibbledata::aus_production \%>\% 323 | model(X_13ARIMA_SEATS(Beer)) 324 | 325 | report(fit) 326 | components(fit) 327 | 328 | # Additive X-11 decomposition 329 | fit <- tsibbledata::aus_production \%>\% 330 | model(X_13ARIMA_SEATS(Beer ~ transform(`function` = "none") + x11(mode = "add"))) 331 | 332 | report(fit) 333 | components(fit) 334 | 335 | } 336 | 337 | } 338 | \references{ 339 | Gomez, Victor, and Agustin Maravall. "Automatic modeling methods for 340 | univariate series." A course in time series analysis (2001): 171-201. 341 | 342 | Dagum, E.B. (1988), The X11 ARIMA/88 Seasonal Adjustment Method - Foundations 343 | And User’s Manual, Time Series Research and Analysis Division Statistics 344 | Canada, Ottawa. 345 | 346 | Dagum, E. B., & Bianconcini, S. (2016) "Seasonal adjustment methods and real 347 | time trend-cycle estimation". \emph{Springer}. 348 | 349 | X-13ARIMA-SEATS Documentation from the seasonal package's website: 350 | http://www.seasonal.website/seasonal.html 351 | 352 | Official X-13ARIMA-SEATS manual: \url{https://www2.census.gov/software/x-13arima-seats/x13as/windows/documentation/docx13as.pdf} 353 | } 354 | \seealso{ 355 | \code{\link[seasonal:seas]{seasonal::seas()}} 356 | } 357 | -------------------------------------------------------------------------------- /man/autoplot.tbl_cf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/acf.R 3 | \name{autoplot.tbl_cf} 4 | \alias{autoplot.tbl_cf} 5 | \title{Auto- and Cross- Covariance and -Correlation plots} 6 | \usage{ 7 | \method{autoplot}{tbl_cf}(object, level = 95, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A tbl_cf object (the result \code{\link[=ACF]{ACF()}}, \code{\link[=PACF]{PACF()}}, or \code{\link[=CCF]{CCF()}}).} 11 | 12 | \item{level}{The level of confidence for the blue dashed lines.} 13 | 14 | \item{...}{Unused.} 15 | } 16 | \value{ 17 | A ggplot object showing the correlations. 18 | } 19 | \description{ 20 | Produces an appropriate plot for the result of \code{\link[=ACF]{ACF()}}, \code{\link[=PACF]{PACF()}}, or \code{\link[=CCF]{CCF()}}. 21 | } 22 | -------------------------------------------------------------------------------- /man/classical_decomposition.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/classical.R 3 | \name{classical_decomposition} 4 | \alias{classical_decomposition} 5 | \title{Classical Seasonal Decomposition by Moving Averages} 6 | \usage{ 7 | classical_decomposition(formula, type = c("additive", "multiplicative"), ...) 8 | } 9 | \arguments{ 10 | \item{formula}{Decomposition specification (see "Specials" section).} 11 | 12 | \item{type}{The type of seasonal component. Can be abbreviated.} 13 | 14 | \item{...}{Other arguments passed to \code{\link[stats:decompose]{stats::decompose()}}.} 15 | } 16 | \value{ 17 | A \code{\link[fabletools:dable]{fabletools::dable()}} containing the decomposed trend, seasonality 18 | and remainder from the classical decomposition. 19 | } 20 | \description{ 21 | Decompose a time series into seasonal, trend and irregular components 22 | using moving averages. Deals with additive or multiplicative 23 | seasonal component. 24 | } 25 | \details{ 26 | The additive model used is: 27 | \deqn{Y_t = T_t + S_t + e_t}{Y[t] = T[t] + S[t] + e[t]} 28 | The multiplicative model used is: 29 | \deqn{Y_t = T_t\,S_t\, e_t}{Y[t] = T[t] * S[t] * e[t]} 30 | 31 | The function first determines the trend component using a moving 32 | average (if \code{filter} is \code{NULL}, a symmetric window with 33 | equal weights is used), and removes it from the time series. Then, 34 | the seasonal figure is computed by averaging, for each time unit, over 35 | all periods. The seasonal figure is then centered. Finally, the error 36 | component is determined by removing trend and seasonal figure 37 | (recycled as needed) from the original time series. 38 | 39 | This only works well if \code{x} covers an integer number of complete 40 | periods. 41 | } 42 | \section{Specials}{ 43 | 44 | 45 | \subsection{season}{ 46 | The \code{season} special is used to specify seasonal attributes of the decomposition. 47 | \preformatted{ 48 | season(period = NULL) 49 | } 50 | 51 | \tabular{ll}{ 52 | \code{period} \tab The periodic nature of the seasonality. This can be either a number indicating the number of observations in each seasonal period, or text to indicate the duration of the seasonal window (for example, annual seasonality would be "1 year"). 53 | } 54 | } 55 | } 56 | 57 | \examples{ 58 | as_tsibble(USAccDeaths) \%>\% 59 | model(classical_decomposition(value)) \%>\% 60 | components() 61 | 62 | as_tsibble(USAccDeaths) \%>\% 63 | model(classical_decomposition(value ~ season(12), type = "mult")) \%>\% 64 | components() 65 | 66 | } 67 | -------------------------------------------------------------------------------- /man/coef_hurst.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{coef_hurst} 4 | \alias{coef_hurst} 5 | \title{Hurst coefficient} 6 | \usage{ 7 | coef_hurst(x) 8 | } 9 | \arguments{ 10 | \item{x}{a vector. If missing values are present, the largest 11 | contiguous portion of the vector is used.} 12 | } 13 | \value{ 14 | A numeric value. 15 | } 16 | \description{ 17 | Computes the Hurst coefficient indicating the level of fractional differencing 18 | of a time series. 19 | } 20 | \author{ 21 | Rob J Hyndman 22 | } 23 | -------------------------------------------------------------------------------- /man/cointegration_johansen.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{cointegration_johansen} 4 | \alias{cointegration_johansen} 5 | \title{Johansen Procedure for VAR} 6 | \usage{ 7 | cointegration_johansen(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{Data matrix to be investigated for cointegration.} 11 | 12 | \item{...}{Additional arguments passed to \code{\link[urca:ca.jo]{urca::ca.jo()}}.} 13 | } 14 | \value{ 15 | An object of class \code{ca.jo}. 16 | } 17 | \description{ 18 | Conducts the Johansen procedure on a given data set. The 19 | \code{"trace"} or \code{"eigen"} statistics are reported and the 20 | matrix of eigenvectors as well as the loading matrix. 21 | } 22 | \details{ 23 | Given a general VAR of the form: 24 | 25 | \deqn{\bold{X}_t = \bold{\Pi}_1 \bold{X}_{t-1} + \dots + \bold{\Pi}_k 26 | \bold{X}_{t-k} + \bold{\mu} + \bold{\Phi D}_t + \bold{\varepsilon}_t 27 | , \quad (t = 1, \dots, T),} 28 | 29 | the following two specifications of a VECM exist: 30 | 31 | \deqn{\Delta \bold{X}_t = \bold{\Gamma}_1 \Delta \bold{X}_{t-1} + 32 | \dots + \bold{\Gamma}_{k-1} \Delta \bold{X}_{t-k+1} + \bold{\Pi 33 | X}_{t-k} + \bold{\mu} + \bold{\Phi D}_t + \bold{\varepsilon}_t} 34 | 35 | where 36 | 37 | \deqn{\bold{\Gamma}_i = - (\bold{I} - \bold{\Pi}_1 - \dots - 38 | \bold{\Pi}_i), \quad (i = 1, \dots , k-1),} 39 | 40 | and 41 | 42 | \deqn{\bold{\Pi} = -(\bold{I} - \bold{\Pi}_1 - \dots - \bold{\Pi}_k)} 43 | 44 | The \eqn{\bold{\Gamma}_i} matrices contain the cumulative long-run 45 | impacts, hence if \code{spec="longrun"} is choosen, the above VECM is 46 | estimated. 47 | 48 | The other VECM specification is of the form: 49 | 50 | \deqn{\Delta \bold{X}_t = \bold{\Gamma}_1 \Delta \bold{X}_{t-1} + 51 | \dots + \bold{\Gamma}_{k-1} \Delta \bold{X}_{t-k+1} + \bold{\Pi 52 | X}_{t-1} + \bold{\mu} + \bold{\Phi D}_t + \bold{\varepsilon}_t} 53 | 54 | where 55 | 56 | \deqn{\bold{\Gamma}_i = - (\bold{\Pi}_{i+1} + \dots + \bold{\Pi}_k), 57 | \quad(i = 1, \dots , k-1),} 58 | 59 | and 60 | 61 | \deqn{\bold{\Pi} = -(\bold{I} - \bold{\Pi}_1 - \dots - \bold{\Pi}_k).} 62 | 63 | The \eqn{\bold{\Pi}} matrix is the same as in the first specification. 64 | However, the \eqn{\bold{\Gamma}_i} matrices now differ, in the sense 65 | that they measure transitory effects, hence by setting 66 | \code{spec="transitory"} the second VECM form is estimated. Please note 67 | that inferences drawn on \eqn{\bold{\Pi}} will be the same, regardless 68 | which specification is choosen and that the explanatory power is the 69 | same, too. 70 | 71 | If \code{"season"} is not NULL, centered seasonal dummy variables are 72 | included. 73 | 74 | If \code{"dumvar"} is not NULL, a matrix of dummy variables is included 75 | in the VECM. Please note, that the number of rows of the matrix 76 | containing the dummy variables must be equal to the row number of 77 | \code{x}. 78 | 79 | Critical values are only reported for systems with less than 80 | 11 variables and are taken from Osterwald-Lenum. 81 | } 82 | \examples{ 83 | 84 | cointegration_johansen(cbind(mdeaths, fdeaths)) 85 | 86 | 87 | } 88 | \references{ 89 | Johansen, S. (1988), Statistical Analysis of Cointegration Vectors, 90 | \emph{Journal of Economic Dynamics and Control}, \bold{12}, 231--254. 91 | 92 | Johansen, S. and Juselius, K. (1990), Maximum Likelihood Estimation and 93 | Inference on Cointegration -- with Applications to the Demand for 94 | Money, \emph{Oxford Bulletin of Economics and Statistics}, \bold{52, 95 | 2}, 169--210. 96 | 97 | Johansen, S. (1991), Estimation and Hypothesis Testing of 98 | Cointegration Vectors in Gaussian Vector Autoregressive Models, 99 | \emph{Econometrica}, \bold{Vol. 59, No. 6}, 1551--1580. 100 | 101 | Osterwald-Lenum, M. (1992), A Note with Quantiles of the Asymptotic 102 | Distribution of the Maximum Likelihood Cointegration Rank Test 103 | Statistics, \emph{Oxford Bulletin of Economics and Statistics}, 104 | \bold{55, 3}, 461--472. 105 | } 106 | \seealso{ 107 | \code{\link[urca:ca.jo]{urca::ca.jo()}} 108 | } 109 | \author{ 110 | Bernhard Pfaff 111 | } 112 | -------------------------------------------------------------------------------- /man/cointegration_phillips_ouliaris.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{cointegration_phillips_ouliaris} 4 | \alias{cointegration_phillips_ouliaris} 5 | \title{Phillips and Ouliaris Cointegration Test} 6 | \usage{ 7 | cointegration_phillips_ouliaris(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{Matrix of data to be tested.} 11 | 12 | \item{...}{Additional arguments passed to \code{\link[urca:ca.po]{urca::ca.po()}}.} 13 | } 14 | \value{ 15 | An object of class \code{ca.po}. 16 | } 17 | \description{ 18 | Performs the Phillips and Ouliaris \code{"Pu"} and \code{"Pz"} 19 | cointegration test. 20 | } 21 | \details{ 22 | The test \code{"Pz"}, compared to the test \code{"Pu"}, has the 23 | advantage that it is invariant to the normalization of the 24 | cointegration vector, \emph{i.e.} it does not matter which variable 25 | is on the left hand side of the equation. In case convergence 26 | problems are encountered by matrix inversion, one can pass a higher 27 | tolerance level \emph{via} \code{"tol=..."} to the \code{solve()}-function. 28 | } 29 | \examples{ 30 | 31 | cointegration_phillips_ouliaris(cbind(mdeaths, fdeaths)) 32 | 33 | } 34 | \references{ 35 | Phillips, P.C.B. and Ouliaris, S. (1990), Asymptotic Properties of 36 | Residual Based Tests for Cointegration, \emph{Econometrica}, 37 | \bold{Vol. 58, No. 1}, 165--193. 38 | } 39 | \seealso{ 40 | \code{\link[urca:ca.po]{urca::ca.po()}} 41 | } 42 | \author{ 43 | Bernhard Pfaff 44 | } 45 | -------------------------------------------------------------------------------- /man/feasts-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/feasts.R 3 | \docType{package} 4 | \name{feasts-package} 5 | \alias{feasts} 6 | \alias{feasts-package} 7 | \title{feasts: Feature Extraction and Statistics for Time Series} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | Provides a collection of features, decomposition methods, statistical summaries and graphics functions for the analysing tidy time series data. The package name 'feasts' is an acronym comprising of its key features: Feature Extraction And Statistics for Time Series. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{http://feasts.tidyverts.org/} 17 | \item \url{https://github.com/tidyverts/feasts/} 18 | \item Report bugs at \url{https://github.com/tidyverts/feasts/issues} 19 | } 20 | 21 | } 22 | \author{ 23 | \strong{Maintainer}: Mitchell O'Hara-Wild \email{mail@mitchelloharawild.com} 24 | 25 | Authors: 26 | \itemize{ 27 | \item Rob Hyndman 28 | \item Earo Wang 29 | } 30 | 31 | Other contributors: 32 | \itemize{ 33 | \item Di Cook [contributor] 34 | \item Thiyanga Talagala (Correlation features) [contributor] 35 | \item Leanne Chhay (Guerrero's method) [contributor] 36 | } 37 | 38 | } 39 | \keyword{package} 40 | -------------------------------------------------------------------------------- /man/feat_acf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{feat_acf} 4 | \alias{feat_acf} 5 | \title{Autocorrelation-based features} 6 | \usage{ 7 | feat_acf(x, .period = 1, lag_max = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{x}{a univariate time series} 11 | 12 | \item{.period}{The seasonal period (optional)} 13 | 14 | \item{lag_max}{maximum lag at which to calculate the acf. The default is 15 | \code{max(.period, 10L)} for \code{feat_acf}, and \code{max(.period, 5L)} for \code{feat_pacf}} 16 | 17 | \item{...}{Further arguments passed to \code{\link[stats:acf]{stats::acf()}} or \code{\link[stats:acf]{stats::pacf()}}} 18 | } 19 | \value{ 20 | A vector of 6 values: first autocorrelation coefficient and sum of squared of 21 | first ten autocorrelation coefficients of original series, first-differenced series, 22 | and twice-differenced series. 23 | For seasonal data, the autocorrelation coefficient at the first seasonal lag is 24 | also returned. 25 | } 26 | \description{ 27 | Computes various measures based on autocorrelation coefficients of the 28 | original series, first-differenced series and second-differenced series 29 | } 30 | \author{ 31 | Thiyanga Talagala 32 | } 33 | -------------------------------------------------------------------------------- /man/feat_intermittent.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{feat_intermittent} 4 | \alias{feat_intermittent} 5 | \title{Intermittency features} 6 | \usage{ 7 | feat_intermittent(x) 8 | } 9 | \arguments{ 10 | \item{x}{A vector to extract features from.} 11 | } 12 | \value{ 13 | A vector of named features: 14 | \itemize{ 15 | \item zero_run_mean: The average interval between non-zero observations 16 | \item nonzero_squared_cv: The squared coefficient of variation of non-zero observations 17 | \item zero_start_prop: The proportion of data which starts with zero 18 | \item zero_end_prop: The proportion of data which ends with zero 19 | } 20 | } 21 | \description{ 22 | Computes various measures that can indicate the presence and structures of 23 | intermittent data. 24 | } 25 | \references{ 26 | Kostenko, A. V., & Hyndman, R. J. (2006). A note on the categorization of 27 | demand patterns. \emph{Journal of the Operational Research Society}, 57(10), 28 | 1256-1257. 29 | } 30 | -------------------------------------------------------------------------------- /man/feat_pacf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{feat_pacf} 4 | \alias{feat_pacf} 5 | \title{Partial autocorrelation-based features} 6 | \usage{ 7 | feat_pacf(x, .period = 1, lag_max = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{x}{a univariate time series} 11 | 12 | \item{.period}{The seasonal period (optional)} 13 | 14 | \item{lag_max}{maximum lag at which to calculate the acf. The default is 15 | \code{max(.period, 10L)} for \code{feat_acf}, and \code{max(.period, 5L)} for \code{feat_pacf}} 16 | 17 | \item{...}{Further arguments passed to \code{\link[stats:acf]{stats::acf()}} or \code{\link[stats:acf]{stats::pacf()}}} 18 | } 19 | \value{ 20 | A vector of 3 values: Sum of squared of first 5 21 | partial autocorrelation coefficients of the original series, first differenced 22 | series and twice-differenced series. 23 | For seasonal data, the partial autocorrelation coefficient at the first seasonal 24 | lag is also returned. 25 | } 26 | \description{ 27 | Computes various measures based on partial autocorrelation coefficients of the 28 | original series, first-differenced series and second-differenced series. 29 | } 30 | \author{ 31 | Thiyanga Talagala 32 | } 33 | -------------------------------------------------------------------------------- /man/feat_spectral.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{feat_spectral} 4 | \alias{feat_spectral} 5 | \title{Spectral features of a time series} 6 | \usage{ 7 | feat_spectral(x, .period = 1, ...) 8 | } 9 | \arguments{ 10 | \item{x}{a univariate time series} 11 | 12 | \item{.period}{The seasonal period.} 13 | 14 | \item{...}{Further arguments for \code{\link[stats:spec.ar]{stats::spec.ar()}}} 15 | } 16 | \value{ 17 | A non-negative real value for the spectral entropy \eqn{H_s(x_t)}. 18 | } 19 | \description{ 20 | Computes spectral entropy from a univariate normalized 21 | spectral density, estimated using an AR model. 22 | } 23 | \details{ 24 | The \emph{spectral entropy} equals the Shannon entropy of the spectral density 25 | \eqn{f_x(\lambda)} of a stationary process \eqn{x_t}: 26 | \deqn{ 27 | H_s(x_t) = - \int_{-\pi}^{\pi} f_x(\lambda) \log f_x(\lambda) d \lambda, 28 | } 29 | where the density is normalized such that 30 | \eqn{\int_{-\pi}^{\pi} f_x(\lambda) d \lambda = 1}. 31 | An estimate of \eqn{f(\lambda)} can be obtained using \code{\link[stats]{spec.ar}} with 32 | the \code{burg} method. 33 | } 34 | \examples{ 35 | feat_spectral(rnorm(1000)) 36 | feat_spectral(lynx) 37 | feat_spectral(sin(1:20)) 38 | } 39 | \references{ 40 | Jerry D. Gibson and Jaewoo Jung (2006). \dQuote{The 41 | Interpretation of Spectral Entropy Based Upon Rate Distortion Functions}. 42 | IEEE International Symposium on Information Theory, pp. 277-281. 43 | 44 | Goerg, G. M. (2013). \dQuote{Forecastable Component Analysis}. 45 | Journal of Machine Learning Research (JMLR) W&CP 28 (2): 64-72, 2013. 46 | Available at \url{https://proceedings.mlr.press/v28/goerg13.html}. 47 | } 48 | \seealso{ 49 | \code{\link[stats]{spec.ar}} 50 | } 51 | \author{ 52 | Rob J Hyndman 53 | } 54 | -------------------------------------------------------------------------------- /man/feat_stl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{feat_stl} 4 | \alias{feat_stl} 5 | \title{STL features} 6 | \usage{ 7 | feat_stl(x, .period, s.window = 11, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A vector to extract features from.} 11 | 12 | \item{.period}{The period of the seasonality.} 13 | 14 | \item{s.window}{The seasonal window of the data (passed to \code{\link[stats:stl]{stats::stl()}})} 15 | 16 | \item{...}{Further arguments passed to \code{\link[stats:stl]{stats::stl()}}} 17 | } 18 | \value{ 19 | A vector of numeric features from a STL decomposition. 20 | } 21 | \description{ 22 | Computes a variety of measures extracted from an STL decomposition of the 23 | time series. This includes details about the strength of trend and seasonality. 24 | } 25 | \seealso{ 26 | \href{https://otexts.com/fpp3/stlfeatures.html}{Forecasting Principle and Practices: Measuring strength of trend and seasonality} 27 | } 28 | -------------------------------------------------------------------------------- /man/figures/README-acf-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-acf-1.png -------------------------------------------------------------------------------- /man/figures/README-dcmp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-dcmp-1.png -------------------------------------------------------------------------------- /man/figures/README-dcmp-plot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-dcmp-plot-1.png -------------------------------------------------------------------------------- /man/figures/README-extreme-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-extreme-1.png -------------------------------------------------------------------------------- /man/figures/README-features-plot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-features-plot-1.png -------------------------------------------------------------------------------- /man/figures/README-graphics-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-graphics-1.png -------------------------------------------------------------------------------- /man/figures/README-graphics-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-graphics-2.png -------------------------------------------------------------------------------- /man/figures/README-graphics-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-graphics-3.png -------------------------------------------------------------------------------- /man/figures/README-graphics-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-graphics-4.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-unnamed-chunk-1-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/README-unnamed-chunk-2-1.png -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesoft-deprecatedsoft-deprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidyverts/feasts/a98cedf214f31ab52f195fb2ec37711b7ffc301f/man/figures/logo.png -------------------------------------------------------------------------------- /man/generate.stl_decomposition.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stl.R 3 | \name{generate.stl_decomposition} 4 | \alias{generate.stl_decomposition} 5 | \title{Generate block bootstrapped series from an STL decomposition} 6 | \usage{ 7 | \method{generate}{stl_decomposition}(x, new_data, specials = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A fitted model.} 11 | 12 | \item{new_data}{A tsibble containing the time points and exogenous regressors to produce forecasts for.} 13 | 14 | \item{specials}{(passed by \code{\link[fabletools:forecast]{fabletools::forecast.mdl_df()}}).} 15 | 16 | \item{...}{Other arguments passed to methods} 17 | } 18 | \description{ 19 | Produces new data with the same structure by resampling the residuals using 20 | a block bootstrap procedure. This method can only generate within sample, and 21 | any generated data out of the trained sample will produce NA simulations. 22 | } 23 | \examples{ 24 | as_tsibble(USAccDeaths) \%>\% 25 | model(STL(log(value))) \%>\% 26 | generate(as_tsibble(USAccDeaths), times = 3) 27 | 28 | } 29 | \references{ 30 | Bergmeir, C., R. J. Hyndman, and J. M. Benitez (2016). Bagging Exponential Smoothing Methods using STL Decomposition and Box-Cox Transformation. International Journal of Forecasting 32, 303-312. 31 | } 32 | -------------------------------------------------------------------------------- /man/gg_arma.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_arma} 4 | \alias{gg_arma} 5 | \title{Plot characteristic ARMA roots} 6 | \usage{ 7 | gg_arma(data) 8 | } 9 | \arguments{ 10 | \item{data}{A mable containing models with AR and/or MA roots.} 11 | } 12 | \value{ 13 | A ggplot object the characteristic roots from ARMA components. 14 | } 15 | \description{ 16 | Produces a plot of the inverse AR and MA roots of an ARIMA model. 17 | Inverse roots outside the unit circle are shown in red. 18 | } 19 | \details{ 20 | Only models which compute ARMA roots can be visualised with this function. 21 | That is to say, the \code{glance()} of the model contains \code{ar_roots} and \code{ma_roots}. 22 | } 23 | \examples{ 24 | if (requireNamespace("fable", quietly = TRUE)) { 25 | library(fable) 26 | library(tsibble) 27 | library(dplyr) 28 | 29 | tsibbledata::aus_retail \%>\% 30 | filter( 31 | State == "Victoria", 32 | Industry == "Cafes, restaurants and catering services" 33 | ) \%>\% 34 | model(ARIMA(Turnover ~ pdq(0,1,1) + PDQ(0,1,1))) \%>\% 35 | gg_arma() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /man/gg_irf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_irf} 4 | \alias{gg_irf} 5 | \title{Plot impulse response functions} 6 | \usage{ 7 | gg_irf(data, y = all_of(measured_vars(data))) 8 | } 9 | \arguments{ 10 | \item{data}{A tsibble with impulse responses} 11 | 12 | \item{y}{The impulse response variables to plot (defaults to all measured variables).} 13 | } 14 | \value{ 15 | A ggplot object of the impulse responses. 16 | } 17 | \description{ 18 | Produces a plot of impulse responses from an impulse response function. 19 | } 20 | -------------------------------------------------------------------------------- /man/gg_lag.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_lag} 4 | \alias{gg_lag} 5 | \title{Lag plots} 6 | \usage{ 7 | gg_lag( 8 | data, 9 | y = NULL, 10 | period = NULL, 11 | lags = 1:9, 12 | geom = c("path", "point"), 13 | arrow = FALSE, 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{data}{A tidy time series object (tsibble)} 19 | 20 | \item{y}{The variable to plot (a bare expression). If NULL, it will 21 | automatically selected from the data.} 22 | 23 | \item{period}{The seasonal period to display. If NULL (default), 24 | the largest frequency in the data is used. If numeric, it represents 25 | the frequency times the interval between observations. If a string 26 | (e.g., "1y" for 1 year, "3m" for 3 months, "1d" for 1 day, 27 | "1h" for 1 hour, "1min" for 1 minute, "1s" for 1 second), 28 | it's converted to a Period class object from the lubridate package. 29 | Note that the data must have at least one observation per seasonal period, 30 | and the period cannot be smaller than the observation interval.} 31 | 32 | \item{lags}{A vector of lags to display as facets.} 33 | 34 | \item{geom}{The geometry used to display the data.} 35 | 36 | \item{arrow}{Arrow specification to show the direction in the lag path. If 37 | TRUE, an appropriate default arrow will be used. Alternatively, a user 38 | controllable arrow created with \code{\link[grid:arrow]{grid::arrow()}} can be used.} 39 | 40 | \item{...}{Additional arguments passed to the geom.} 41 | } 42 | \value{ 43 | A ggplot object showing a lag plot of a time series. 44 | } 45 | \description{ 46 | A lag plot shows the time series against lags of itself. It is often coloured 47 | the seasonal period to identify how each season correlates with others. 48 | } 49 | \examples{ 50 | library(tsibble) 51 | library(dplyr) 52 | tsibbledata::aus_retail \%>\% 53 | filter( 54 | State == "Victoria", 55 | Industry == "Cafes, restaurants and catering services" 56 | ) \%>\% 57 | gg_lag(Turnover) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /man/gg_season.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_season} 4 | \alias{gg_season} 5 | \title{Seasonal plot} 6 | \usage{ 7 | gg_season( 8 | data, 9 | y = NULL, 10 | period = NULL, 11 | facet_period = NULL, 12 | max_col = Inf, 13 | max_col_discrete = 7, 14 | pal = (scales::hue_pal())(9), 15 | polar = FALSE, 16 | labels = c("none", "left", "right", "both"), 17 | labels_repel = FALSE, 18 | labels_left_nudge = 0, 19 | labels_right_nudge = 0, 20 | ... 21 | ) 22 | } 23 | \arguments{ 24 | \item{data}{A tidy time series object (tsibble)} 25 | 26 | \item{y}{The variable to plot (a bare expression). If NULL, it will 27 | automatically selected from the data.} 28 | 29 | \item{period}{The seasonal period to display. If NULL (default), 30 | the largest frequency in the data is used. If numeric, it represents 31 | the frequency times the interval between observations. If a string 32 | (e.g., "1y" for 1 year, "3m" for 3 months, "1d" for 1 day, 33 | "1h" for 1 hour, "1min" for 1 minute, "1s" for 1 second), 34 | it's converted to a Period class object from the lubridate package. 35 | Note that the data must have at least one observation per seasonal period, 36 | and the period cannot be smaller than the observation interval.} 37 | 38 | \item{facet_period}{A secondary seasonal period to facet by 39 | (typically smaller than period).} 40 | 41 | \item{max_col}{The maximum number of colours to display on the plot. If the 42 | number of seasonal periods in the data is larger than \code{max_col}, the plot 43 | will not include a colour. Use \code{max_col = 0} to never colour the lines, or Inf 44 | to always colour the lines. If labels are used, then max_col will be ignored.} 45 | 46 | \item{max_col_discrete}{The maximum number of colours to show using a discrete colour scale.} 47 | 48 | \item{pal}{A colour palette to be used.} 49 | 50 | \item{polar}{If TRUE, the season plot will be shown on polar coordinates.} 51 | 52 | \item{labels}{Position of the labels for seasonal period identifier.} 53 | 54 | \item{labels_repel}{If TRUE, the seasonal period identifying labels will be repelled with the ggrepel package.} 55 | 56 | \item{labels_left_nudge, labels_right_nudge}{Allows seasonal period identifying labels to be nudged to the left or right from their default position.} 57 | 58 | \item{...}{Additional arguments passed to geom_line()} 59 | } 60 | \value{ 61 | A ggplot object showing a seasonal plot of a time series. 62 | } 63 | \description{ 64 | Produces a time series seasonal plot. A seasonal plot is similar to a regular 65 | time series plot, except the x-axis shows data from within each season. This 66 | plot type allows the underlying seasonal pattern to be seen more clearly, 67 | and is especially useful in identifying years in which the pattern changes. 68 | } 69 | \examples{ 70 | library(tsibble) 71 | library(dplyr) 72 | tsibbledata::aus_retail \%>\% 73 | filter( 74 | State == "Victoria", 75 | Industry == "Cafes, restaurants and catering services" 76 | ) \%>\% 77 | gg_season(Turnover) 78 | 79 | } 80 | \references{ 81 | Hyndman and Athanasopoulos (2019) Forecasting: principles and practice, 82 | 3rd edition, OTexts: Melbourne, Australia. https://OTexts.com/fpp3/ 83 | } 84 | -------------------------------------------------------------------------------- /man/gg_subseries.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_subseries} 4 | \alias{gg_subseries} 5 | \title{Seasonal subseries plots} 6 | \usage{ 7 | gg_subseries(data, y = NULL, period = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{data}{A tidy time series object (tsibble)} 11 | 12 | \item{y}{The variable to plot (a bare expression). If NULL, it will 13 | automatically selected from the data.} 14 | 15 | \item{period}{The seasonal period to display. If NULL (default), 16 | the largest frequency in the data is used. If numeric, it represents 17 | the frequency times the interval between observations. If a string 18 | (e.g., "1y" for 1 year, "3m" for 3 months, "1d" for 1 day, 19 | "1h" for 1 hour, "1min" for 1 minute, "1s" for 1 second), 20 | it's converted to a Period class object from the lubridate package. 21 | Note that the data must have at least one observation per seasonal period, 22 | and the period cannot be smaller than the observation interval.} 23 | 24 | \item{...}{Additional arguments passed to geom_line()} 25 | } 26 | \value{ 27 | A ggplot object showing a seasonal subseries plot of a time series. 28 | } 29 | \description{ 30 | A seasonal subseries plot facets the time series by each season in the 31 | seasonal period. These facets form smaller time series plots consisting of 32 | data only from that season. If you had several years of monthly data, the 33 | resulting plot would show a separate time series plot for each month. The 34 | first subseries plot would consist of only data from January. This case is 35 | given as an example below. 36 | } 37 | \details{ 38 | The horizontal lines are used to represent the mean of each facet, allowing 39 | easy identification of seasonal differences between seasons. This plot is 40 | particularly useful in identifying changes in the seasonal pattern over time. 41 | 42 | similar to a seasonal plot (\code{\link[=gg_season]{gg_season()}}), and 43 | } 44 | \examples{ 45 | library(tsibble) 46 | library(dplyr) 47 | tsibbledata::aus_retail \%>\% 48 | filter( 49 | State == "Victoria", 50 | Industry == "Cafes, restaurants and catering services" 51 | ) \%>\% 52 | gg_subseries(Turnover) 53 | 54 | } 55 | \references{ 56 | Hyndman and Athanasopoulos (2019) Forecasting: principles and practice, 57 | 3rd edition, OTexts: Melbourne, Australia. https://OTexts.com/fpp3/ 58 | } 59 | -------------------------------------------------------------------------------- /man/gg_tsdisplay.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_tsdisplay} 4 | \alias{gg_tsdisplay} 5 | \title{Ensemble of time series displays} 6 | \usage{ 7 | gg_tsdisplay( 8 | data, 9 | y = NULL, 10 | plot_type = c("auto", "partial", "season", "histogram", "scatter", "spectrum"), 11 | lag_max = NULL 12 | ) 13 | } 14 | \arguments{ 15 | \item{data}{A tidy time series object (tsibble)} 16 | 17 | \item{y}{The variable to plot (a bare expression). If NULL, it will 18 | automatically selected from the data.} 19 | 20 | \item{plot_type}{type of plot to include in lower right corner. By default 21 | (\code{"auto"}) a season plot will be shown for seasonal data, a spectrum plot 22 | will be shown for non-seasonal data without missing values, and a PACF will 23 | be shown otherwise.} 24 | 25 | \item{lag_max}{maximum lag at which to calculate the acf. Default is 10*log10(N/m) 26 | where N is the number of observations and m the number of series. Will be 27 | automatically limited to one less than the number of observations in the series.} 28 | } 29 | \value{ 30 | A list of ggplot objects showing useful plots of a time series. 31 | } 32 | \description{ 33 | Plots a time series along with its ACF along with an customisable third 34 | graphic of either a PACF, histogram, lagged scatterplot or spectral density. 35 | } 36 | \examples{ 37 | library(tsibble) 38 | library(dplyr) 39 | tsibbledata::aus_retail \%>\% 40 | filter( 41 | State == "Victoria", 42 | Industry == "Cafes, restaurants and catering services" 43 | ) \%>\% 44 | gg_tsdisplay(Turnover) 45 | 46 | } 47 | \references{ 48 | Hyndman and Athanasopoulos (2019) \emph{Forecasting: principles 49 | and practice}, 3rd edition, OTexts: Melbourne, Australia. 50 | \url{https://OTexts.com/fpp3/} 51 | } 52 | \seealso{ 53 | \code{\link[stats]{plot.ts}}, \code{\link{ACF}}, 54 | \code{\link[stats]{spec.ar}} 55 | } 56 | \author{ 57 | Rob J Hyndman & Mitchell O'Hara-Wild 58 | } 59 | -------------------------------------------------------------------------------- /man/gg_tsresiduals.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graphics.R 3 | \name{gg_tsresiduals} 4 | \alias{gg_tsresiduals} 5 | \title{Ensemble of time series residual diagnostic plots} 6 | \usage{ 7 | gg_tsresiduals(data, type = "innovation", plot_type = "histogram", ...) 8 | } 9 | \arguments{ 10 | \item{data}{A mable containing one model with residuals.} 11 | 12 | \item{type}{The type of residuals to compute. If \code{type="response"}, residuals on the back-transformed data will be computed.} 13 | 14 | \item{plot_type}{type of plot to include in lower right corner. By default 15 | (\code{"auto"}) a season plot will be shown for seasonal data, a spectrum plot 16 | will be shown for non-seasonal data without missing values, and a PACF will 17 | be shown otherwise.} 18 | 19 | \item{...}{Additional arguments passed to \code{\link[=gg_tsdisplay]{gg_tsdisplay()}}.} 20 | } 21 | \value{ 22 | A list of ggplot objects showing a useful plots of a time series model's residuals. 23 | } 24 | \description{ 25 | Plots the residuals using a time series plot, ACF and histogram. 26 | } 27 | \examples{ 28 | if (requireNamespace("fable", quietly = TRUE)) { 29 | library(fable) 30 | 31 | tsibbledata::aus_production \%>\% 32 | model(ETS(Beer)) \%>\% 33 | gg_tsresiduals() 34 | } 35 | 36 | } 37 | \references{ 38 | Hyndman and Athanasopoulos (2019) \emph{Forecasting: principles 39 | and practice}, 3rd edition, OTexts: Melbourne, Australia. 40 | \url{https://OTexts.com/fpp3/} 41 | } 42 | \seealso{ 43 | \code{\link[=gg_tsdisplay]{gg_tsdisplay()}} 44 | } 45 | -------------------------------------------------------------------------------- /man/guerrero.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/guerrero.R 3 | \name{guerrero} 4 | \alias{guerrero} 5 | \title{Guerrero's method for Box Cox lambda selection} 6 | \usage{ 7 | guerrero(x, lower = -0.9, upper = 2, .period = 2L) 8 | } 9 | \arguments{ 10 | \item{x}{A numeric vector. The data used to identify the transformation 11 | parameter lambda.} 12 | 13 | \item{lower}{The lower bound for lambda.} 14 | 15 | \item{upper}{The upper bound for lambda.} 16 | 17 | \item{.period}{The length of each subseries (usually the length of seasonal 18 | period). Subseries length must be at least 2.} 19 | } 20 | \value{ 21 | A Box Cox transformation parameter (lambda) chosen by Guerrero's method. 22 | } 23 | \description{ 24 | Applies Guerrero's (1993) method to select the lambda which minimises the 25 | coefficient of variation for subseries of x. 26 | } 27 | \details{ 28 | Note that this function will give slightly different results to 29 | \code{forecast::BoxCox.lambda(y)} if your data does not start at the start of the 30 | seasonal period. This function will make use of all of your data, whereas the 31 | forecast package will not use data that doesn't complete a seasonal period. 32 | } 33 | \references{ 34 | Box, G. E. P. and Cox, D. R. (1964) An analysis of transformations. JRSS B 26 211–246. 35 | 36 | Guerrero, V.M. (1993) Time-series analysis supported by power transformations. Journal of Forecasting, 12, 37–48. 37 | } 38 | -------------------------------------------------------------------------------- /man/longest_flat_spot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{longest_flat_spot} 4 | \alias{longest_flat_spot} 5 | \alias{n_flat_spots} 6 | \title{Longest flat spot length} 7 | \usage{ 8 | longest_flat_spot(x) 9 | } 10 | \arguments{ 11 | \item{x}{a vector} 12 | } 13 | \value{ 14 | A numeric value. 15 | } 16 | \description{ 17 | "Flat spots” are computed by dividing the sample space of a time series into 18 | ten equal-sized intervals, and computing the maximum run length within any 19 | single interval. 20 | } 21 | \author{ 22 | Earo Wang and Rob J Hyndman 23 | } 24 | -------------------------------------------------------------------------------- /man/n_crossing_points.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{n_crossing_points} 4 | \alias{n_crossing_points} 5 | \title{Number of crossing points} 6 | \usage{ 7 | n_crossing_points(x) 8 | } 9 | \arguments{ 10 | \item{x}{a univariate time series} 11 | } 12 | \value{ 13 | A numeric value. 14 | } 15 | \description{ 16 | Computes the number of times a time series crosses the median. 17 | } 18 | \author{ 19 | Earo Wang and Rob J Hyndman 20 | } 21 | -------------------------------------------------------------------------------- /man/portmanteau_tests.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tests.R 3 | \docType{data} 4 | \name{ljung_box} 5 | \alias{ljung_box} 6 | \alias{box_pierce} 7 | \alias{portmanteau_tests} 8 | \title{Portmanteau tests} 9 | \format{ 10 | An object of class \code{list} of length 2. 11 | } 12 | \usage{ 13 | ljung_box(x, lag = 1, dof = 0, ...) 14 | 15 | box_pierce(x, lag = 1, dof = 0, ...) 16 | 17 | portmanteau_tests 18 | } 19 | \arguments{ 20 | \item{x}{A numeric vector} 21 | 22 | \item{lag}{The number of lag autocorrelation coefficients to use in calculating the statistic} 23 | 24 | \item{dof}{Degrees of freedom of the fitted model (useful if x is a series of residuals).} 25 | 26 | \item{...}{Unused.} 27 | } 28 | \value{ 29 | A vector of numeric features for the test's statistic and p-value. 30 | } 31 | \description{ 32 | Compute the Box–Pierce or Ljung–Box test statistic for examining the null hypothesis of independence in a given time series. These are sometimes known as ‘portmanteau’ tests. 33 | } 34 | \examples{ 35 | ljung_box(rnorm(100)) 36 | 37 | box_pierce(rnorm(100)) 38 | } 39 | \seealso{ 40 | \code{\link[stats:box.test]{stats::Box.test()}} 41 | } 42 | \keyword{datasets} 43 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reexports.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{\%>\%} 7 | \alias{as_tsibble} 8 | \alias{autoplot} 9 | \alias{autolayer} 10 | \title{Objects exported from other packages} 11 | \keyword{internal} 12 | \description{ 13 | These objects are imported from other packages. Follow the links 14 | below to see their documentation. 15 | 16 | \describe{ 17 | \item{dplyr}{\code{\link[dplyr:reexports]{\%>\%}}} 18 | 19 | \item{ggplot2}{\code{\link[ggplot2]{autolayer}}, \code{\link[ggplot2]{autoplot}}} 20 | 21 | \item{tsibble}{\code{\link[tsibble:as-tsibble]{as_tsibble}}} 22 | }} 23 | 24 | -------------------------------------------------------------------------------- /man/scale_cf_lag.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/acf.R 3 | \name{scale_cf_lag} 4 | \alias{scale_cf_lag} 5 | \alias{scale_x_cf_lag} 6 | \title{lagged datetime scales 7 | This set of scales defines new scales for lagged time structures.} 8 | \usage{ 9 | scale_x_cf_lag(...) 10 | } 11 | \arguments{ 12 | \item{...}{Further arguments to be passed on to scale_x_continuous()} 13 | } 14 | \value{ 15 | A ggproto object inheriting from \code{Scale} 16 | } 17 | \description{ 18 | lagged datetime scales 19 | This set of scales defines new scales for lagged time structures. 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /man/shift_level_max.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{shift_level_max} 4 | \alias{shift_level_max} 5 | \alias{shift_var_max} 6 | \alias{shift_kl_max} 7 | \title{Sliding window features} 8 | \usage{ 9 | shift_level_max(x, .size = NULL, .period = 1) 10 | 11 | shift_var_max(x, .size = NULL, .period = 1) 12 | 13 | shift_kl_max(x, .size = NULL, .period = 1) 14 | } 15 | \arguments{ 16 | \item{x}{a univariate time series} 17 | 18 | \item{.size}{size of sliding window, if NULL \code{.size} will be automatically chosen using \code{.period}} 19 | 20 | \item{.period}{The seasonal period (optional)} 21 | } 22 | \value{ 23 | A vector of 2 values: the size of the shift, and the time index of the shift. 24 | } 25 | \description{ 26 | Computes feature of a time series based on sliding (overlapping) windows. 27 | \code{shift_level_max} finds the largest mean shift between two consecutive windows. 28 | \code{shift_var_max} finds the largest var shift between two consecutive windows. 29 | \code{shift_kl_max} finds the largest shift in Kulback-Leibler divergence between 30 | two consecutive windows. 31 | } 32 | \details{ 33 | Computes the largest level shift and largest variance shift in sliding mean calculations 34 | } 35 | \author{ 36 | Earo Wang, Rob J Hyndman and Mitchell O'Hara-Wild 37 | } 38 | -------------------------------------------------------------------------------- /man/stat_arch_lm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{stat_arch_lm} 4 | \alias{stat_arch_lm} 5 | \title{ARCH LM Statistic} 6 | \usage{ 7 | stat_arch_lm(x, lags = 12, demean = TRUE) 8 | } 9 | \arguments{ 10 | \item{x}{a univariate time series} 11 | 12 | \item{lags}{Number of lags to use in the test} 13 | 14 | \item{demean}{Should data have mean removed before test applied?} 15 | } 16 | \value{ 17 | A numeric value. 18 | } 19 | \description{ 20 | Computes a statistic based on the Lagrange Multiplier (LM) test of Engle (1982) for 21 | autoregressive conditional heteroscedasticity (ARCH). The statistic returned is 22 | the \eqn{R^2}{R^2} value of an autoregressive model of order \code{lags} applied 23 | to \eqn{x^2}{x^2}. 24 | } 25 | \author{ 26 | Yanfei Kang 27 | } 28 | -------------------------------------------------------------------------------- /man/tile_features.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{var_tiled_var} 4 | \alias{var_tiled_var} 5 | \alias{var_tiled_mean} 6 | \title{Time series features based on tiled windows} 7 | \usage{ 8 | var_tiled_var(x, .size = NULL, .period = 1) 9 | 10 | var_tiled_mean(x, .size = NULL, .period = 1) 11 | } 12 | \arguments{ 13 | \item{x}{a univariate time series} 14 | 15 | \item{.size}{size of sliding window, if NULL \code{.size} will be automatically chosen using \code{.period}} 16 | 17 | \item{.period}{The seasonal period (optional)} 18 | } 19 | \value{ 20 | A numeric vector of length 2 containing a measure of lumpiness and 21 | a measure of stability. 22 | } 23 | \description{ 24 | Computes feature of a time series based on tiled (non-overlapping) windows. 25 | Means or variances are produced for all tiled windows. Then stability is 26 | the variance of the means, while lumpiness is the variance of the variances. 27 | } 28 | \author{ 29 | Earo Wang and Rob J Hyndman 30 | } 31 | -------------------------------------------------------------------------------- /man/unitroot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{unitroot_kpss} 4 | \alias{unitroot_kpss} 5 | \alias{unitroot_pp} 6 | \title{Unit root tests} 7 | \usage{ 8 | unitroot_kpss(x, type = c("mu", "tau"), lags = c("short", "long", "nil"), ...) 9 | 10 | unitroot_pp( 11 | x, 12 | type = c("Z-tau", "Z-alpha"), 13 | model = c("constant", "trend"), 14 | lags = c("short", "long"), 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{x}{A vector to be tested for the unit root.} 20 | 21 | \item{type}{Type of deterministic part.} 22 | 23 | \item{lags}{Maximum number of lags used for error term correction.} 24 | 25 | \item{...}{Arguments passed to unit root test function.} 26 | 27 | \item{model}{Determines the deterministic part in the test regression.} 28 | } 29 | \value{ 30 | A vector of numeric features for the test's statistic and p-value. 31 | } 32 | \description{ 33 | Performs a test for the existence of a unit root in the vector. 34 | } 35 | \details{ 36 | \code{unitroot_kpss} computes the statistic for the Kwiatkowski et al. unit root test with linear trend and lag 1. 37 | 38 | \code{unitroot_pp} computes the statistic for the \code{Z-tau} version of Phillips & Perron unit root test with constant trend and lag 1. 39 | } 40 | \seealso{ 41 | \code{\link[urca:ur.kpss]{urca::ur.kpss()}} 42 | 43 | \code{\link[urca:ur.pp]{urca::ur.pp()}} 44 | } 45 | -------------------------------------------------------------------------------- /man/unitroot_ndiffs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/features.R 3 | \name{unitroot_ndiffs} 4 | \alias{unitroot_ndiffs} 5 | \alias{unitroot_nsdiffs} 6 | \title{Number of differences required for a stationary series} 7 | \usage{ 8 | unitroot_ndiffs( 9 | x, 10 | alpha = 0.05, 11 | unitroot_fn = ~unitroot_kpss(.)["kpss_pvalue"], 12 | differences = 0:2, 13 | ... 14 | ) 15 | 16 | unitroot_nsdiffs( 17 | x, 18 | alpha = 0.05, 19 | unitroot_fn = ~feat_stl(., .period)[2] < 0.64, 20 | differences = 0:2, 21 | .period = 1, 22 | ... 23 | ) 24 | } 25 | \arguments{ 26 | \item{x}{A vector to be tested for the unit root.} 27 | 28 | \item{alpha}{The level of the test.} 29 | 30 | \item{unitroot_fn}{A function (or lambda) that provides a p-value for a unit root test.} 31 | 32 | \item{differences}{The possible differences to consider.} 33 | 34 | \item{...}{Additional arguments passed to the \code{unitroot_fn} function} 35 | 36 | \item{.period}{The period of the seasonality.} 37 | } 38 | \value{ 39 | A numeric corresponding to the minimum required differences for stationarity. 40 | } 41 | \description{ 42 | Use a unit root function to determine the minimum number of differences 43 | necessary to obtain a stationary time series. 44 | } 45 | \details{ 46 | Note that the default 'unit root function' for \code{unitroot_nsdiffs()} is based 47 | on the seasonal strength of an STL decomposition. This is not a test for the 48 | presence of a seasonal unit root, but generally works reasonably well in 49 | identifying the presence of seasonality and the need for a seasonal 50 | difference. 51 | } 52 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(feasts) 3 | 4 | test_check("feasts") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-cf.R: -------------------------------------------------------------------------------- 1 | context("test-cf") 2 | 3 | dt <- tsibble::tsibble(idx = 1:25, y = rnorm(25), x = rnorm(25), index = idx) 4 | 5 | test_that("ACF", { 6 | cf <- ACF(dt, y) 7 | expect_identical( 8 | cf$acf, 9 | as.numeric(stats::acf(dt$y, plot = FALSE)$acf)[-1] 10 | ) 11 | 12 | p <- autoplot(cf) 13 | expect_identical( 14 | ggplot2::layer_data(p)$y, 15 | cf$acf 16 | ) 17 | 18 | expect_s3_class( 19 | ggplot2::ggplot_build(p), 20 | "ggplot_built" 21 | ) 22 | 23 | expect_message( 24 | NROW(ACF(dt, lag_max = 5)), 25 | "Response variable not specified" 26 | ) %>% 27 | expect_identical(5L) 28 | 29 | expect_warning( 30 | ACF(dt, y, x), 31 | "ACF currently only supports one column" 32 | ) 33 | }) 34 | 35 | test_that("PACF", { 36 | cf <- PACF(dt, y) 37 | expect_identical( 38 | cf$pacf, 39 | as.numeric(stats::pacf(dt$y, plot = FALSE)$acf) 40 | ) 41 | 42 | acf <- ACF(dt, y, type = "part") # Testing also partial matching of "partial" 43 | names(acf) <- c("lag", "pacf") # Overwrite names c("lag", "acf") 44 | expect_identical( 45 | as_tsibble(cf), 46 | acf 47 | ) 48 | 49 | p <- autoplot(cf) 50 | expect_identical( 51 | ggplot2::layer_data(p)$y, 52 | cf$pacf 53 | ) 54 | 55 | expect_s3_class( 56 | ggplot2::ggplot_build(p), 57 | "ggplot_built" 58 | ) 59 | 60 | expect_message( 61 | NROW(PACF(dt, lag_max = 5)), 62 | "Response variable not specified" 63 | ) %>% 64 | expect_identical(5L) 65 | 66 | expect_warning( 67 | PACF(dt, y, x), 68 | "PACF currently only supports one column" 69 | ) 70 | }) 71 | 72 | 73 | test_that("CCF", { 74 | cf <- CCF(dt, y, x) 75 | expect_identical( 76 | cf$ccf, 77 | as.numeric(stats::ccf(dt$x, dt$y, plot = FALSE)$acf) 78 | ) 79 | 80 | p <- autoplot(cf) 81 | expect_identical( 82 | ggplot2::layer_data(p)$y, 83 | cf$ccf 84 | ) 85 | 86 | expect_s3_class( 87 | ggplot2::ggplot_build(p), 88 | "ggplot_built" 89 | ) 90 | 91 | expect_message( 92 | NROW(CCF(dt, lag_max = 5)), 93 | "Response variable not specified" 94 | ) %>% 95 | expect_identical(11L) 96 | 97 | expect_warning( 98 | CCF(dt, y, x, x), 99 | "CCF currently only supports two columns" 100 | ) 101 | }) 102 | 103 | -------------------------------------------------------------------------------- /tests/testthat/test-classical.R: -------------------------------------------------------------------------------- 1 | context("test-classical") 2 | 3 | test_that("Additive classical decomposition", { 4 | tsbl_co2 <- as_tsibble(co2) 5 | dcmp <- tsbl_co2 %>% model(classical_decomposition(value)) %>% components() 6 | stats_dcmp <- stats::decompose(co2) 7 | 8 | expect_equivalent( 9 | dcmp$trend, 10 | unclass(stats_dcmp$trend) 11 | ) 12 | expect_equivalent( 13 | dcmp$seasonal, 14 | unclass(stats_dcmp$seasonal) 15 | ) 16 | expect_equivalent( 17 | dcmp$random, 18 | unclass(stats_dcmp$random) 19 | ) 20 | expect_equal( 21 | dcmp$value - dcmp$seasonal, 22 | dcmp$season_adjust 23 | ) 24 | }) 25 | 26 | test_that("Multiplicative classical decomposition", { 27 | tsbl_uad <- as_tsibble(USAccDeaths) 28 | dcmp <- tsbl_uad %>% model(classical_decomposition(value, type = "multiplicative")) %>% components() 29 | stats_dcmp <- stats::decompose(USAccDeaths, type = "multiplicative") 30 | 31 | expect_equivalent( 32 | dcmp$trend, 33 | unclass(stats_dcmp$trend) 34 | ) 35 | expect_equivalent( 36 | dcmp$seasonal, 37 | unclass(stats_dcmp$seasonal) 38 | ) 39 | expect_equivalent( 40 | dcmp$random, 41 | unclass(stats_dcmp$random) 42 | ) 43 | expect_equal( 44 | dcmp$value / dcmp$seasonal, 45 | dcmp$season_adjust 46 | ) 47 | }) 48 | -------------------------------------------------------------------------------- /tests/testthat/test-features.R: -------------------------------------------------------------------------------- 1 | context("test-features") 2 | 3 | www_usage <- as_tsibble(WWWusage) 4 | lung_deaths_long <- as_tsibble(cbind(mdeaths, fdeaths)) 5 | lung_deaths_wide <- as_tsibble(cbind(mdeaths, fdeaths), pivot_longer = FALSE) 6 | 7 | test_that("guerrero()", { 8 | ft <- features(www_usage, value, guerrero) 9 | expect_equal(dim(ft), c(1,1)) 10 | expect_equal(ft$lambda_guerrero, 0.360, tolerance = 0.001) 11 | 12 | ft <- features(lung_deaths_long, value, guerrero) 13 | expect_equal(dim(ft), c(2,2)) 14 | expect_equal(ft$lambda_guerrero, c(-0.9, 0.321), tolerance = 0.001) 15 | }) 16 | 17 | test_that("unit root features", { 18 | ft <- features(www_usage, value, list(unitroot_kpss, unitroot_pp, unitroot_ndiffs)) 19 | expect_equal(ft$kpss_pvalue < 0.05, as.logical(ft$ndiffs)) 20 | expect_equal(ft$pp_pvalue, 0.1) 21 | 22 | ft <- features(lung_deaths_long, value, list(feat_stl, unitroot_nsdiffs)) 23 | expect_equal(ft$seasonal_strength_year >= 0.64, as.logical(ft$nsdiffs)) 24 | }) 25 | 26 | test_that("basic features", { 27 | basic_features <- list(n_crossing_points, longest_flat_spot, feat_spectral, 28 | var_tiled_var, var_tiled_mean) 29 | ft <- features(www_usage, value, basic_features) 30 | expect_equivalent( 31 | as.list(ft), 32 | list(n_crossing_points = 7L, longest_flat_spot = 13L, entropy = 0.461, var_tiled_var = 0.0139, var_tiled_mean = 0.988), 33 | tolerance = 0.01 34 | ) 35 | }) 36 | 37 | 38 | test_that("*cf features", { 39 | cf_features <- list(feat_acf, feat_pacf) 40 | ft <- features(www_usage, value, cf_features) 41 | expect_equivalent( 42 | as.list(ft), 43 | list(acf1 = 0.960, acf10 = 4.19, 44 | diff1_acf1 = 0.792, diff1_acf10 = 1.41, 45 | diff2_acf1 = 0.174, diff2_acf10 = 0.334, 46 | pacf5 = 1.04, diff1x_pacf5 = 0.802, diff2x_pacf5 = 0.222), 47 | tolerance = 0.01 48 | ) 49 | }) 50 | 51 | 52 | test_that("*shift features", { 53 | shift_features <- list(shift_level_max, shift_var_max, shift_kl_max) 54 | ft <- features(www_usage, value, shift_features) 55 | expect_equivalent( 56 | as.list(ft), 57 | list(shift_level_max = 71.7, shift_level_index = 84, 58 | shift_var_max = 749, shift_var_index = 54, 59 | shift_kl_max = 1.44, shift_kl_index = 57), 60 | tolerance = 0.01 61 | ) 62 | }) 63 | 64 | test_that("model based features", { 65 | model_features <- list(stat_arch_lm, coef_hurst, feat_stl) 66 | ft <- features(www_usage, value, model_features) 67 | expect_equivalent( 68 | as.list(ft), 69 | list( 70 | stat_arch_lm = 0.990, coef_hurst = 0.998, 71 | trend_strength = 0.985, spikiness = 0.0772, 72 | linearity = 178, curvature = 44, 73 | stl_e_acf1 = 0.774, stl_e_acf10 = 0.983), 74 | tolerance = 0.01 75 | ) 76 | 77 | ft <- features(lung_deaths_wide, fdeaths, feat_stl) 78 | expect_equivalent( 79 | as.list(ft), 80 | list(trend_strength = 0.1191, seasonal_strength_year = 0.8832, 81 | seasonal_peak_year = 2, seasonal_trough_year = 8, spikiness = 23968.457, 82 | linearity = -147.5444, curvature = 13.0048, stl_e_acf1 = 0.024, 83 | stl_e_acf10 = 0.194), 84 | tolerance = 0.01 85 | ) 86 | }) 87 | -------------------------------------------------------------------------------- /tests/testthat/test-graphics.R: -------------------------------------------------------------------------------- 1 | context("test-graphics") 2 | 3 | tsbl_co2 <- as_tsibble(co2) 4 | tsbl_ped <- tsibble::pedestrian %>% 5 | filter(Sensor == "Southern Cross Station", 6 | yearmonth(Date) == yearmonth("2015 Jan")) 7 | 8 | test_that("gg_season() plots", { 9 | p <- gg_season(tsbl_co2, value) 10 | 11 | expect_equal( 12 | ggplot2::layer_data(p)$y, 13 | tsbl_co2$value 14 | ) 15 | expect_equivalent( 16 | as.numeric(table(ggplot2::layer_data(p)$colour)), 17 | rep(12, 39) 18 | ) 19 | 20 | p_built <- ggplot2::ggplot_build(p) 21 | 22 | expect_equivalent( 23 | p_built$plot$labels[c("x", "y")], 24 | list(x = "index", y = "value") 25 | ) 26 | 27 | p <- gg_season(tsbl_ped, Count, "day") 28 | 29 | expect_equal( 30 | ggplot2::layer_data(p)$y, 31 | tsbl_ped$Count 32 | ) 33 | expect_equivalent( 34 | as.numeric(table(ggplot2::layer_data(p)$colour)), 35 | rep(24, 31) 36 | ) 37 | 38 | p_built <- ggplot2::ggplot_build(p) 39 | 40 | expect_equivalent( 41 | p_built$plot$labels[c("x", "y")], 42 | list(x = "Date_Time", y = "Count") 43 | ) 44 | }) 45 | 46 | 47 | test_that("gg_season() plot labels", { 48 | p <- gg_season(tsbl_co2, value, labels = "both") 49 | 50 | expect_equal( 51 | ggplot2::layer_data(p)$y, 52 | tsbl_co2$value 53 | ) 54 | expect_equivalent( 55 | as.numeric(table(ggplot2::layer_data(p)$colour)), 56 | rep(12, 39) 57 | ) 58 | expect_equal( 59 | c(ggplot2::layer_data(p,2)$label, ggplot2::layer_data(p,3)$label), 60 | ordered(rep(1959:1997, 2)) 61 | ) 62 | 63 | p_built <- ggplot2::ggplot_build(p) 64 | 65 | expect_equivalent( 66 | p_built$plot$labels[c("x", "y")], 67 | list(x = "index", y = "value") 68 | ) 69 | }) 70 | 71 | test_that("gg_season() facets", { 72 | p <- gg_season(tsbl_co2, value, facet_period = "10 year", labels = "both") 73 | 74 | # expect_equal( 75 | # ggplot2::layer_data(p)$y, 76 | # tsbl_co2$value 77 | # ) 78 | expect_equivalent( 79 | as.numeric(table(ggplot2::layer_data(p)$colour)), 80 | rep(12, 39) 81 | ) 82 | expect_equal( 83 | c(ggplot2::layer_data(p,2)$label, ggplot2::layer_data(p,3)$label), 84 | ordered(rep(1959:1997, 2)) 85 | ) 86 | 87 | p_built <- ggplot2::ggplot_build(p) 88 | 89 | expect_equivalent( 90 | p_built$plot$labels[c("x", "y")], 91 | list(x = "index", y = "value") 92 | ) 93 | }) 94 | 95 | test_that("gg_subseries() plots", { 96 | p <- gg_subseries(tsbl_co2, value) 97 | 98 | expect_equal( 99 | ggplot2::layer_data(p)$y, 100 | tsbl_co2$value[order((seq_along(tsbl_co2$value) - 1)%%12)] 101 | ) 102 | expect_equivalent( 103 | as.numeric(table(ggplot2::layer_data(p)$PANEL)), 104 | rep(39, 12) 105 | ) 106 | 107 | p_built <- ggplot2::ggplot_build(p) 108 | 109 | expect_equivalent( 110 | p_built$plot$labels[c("x", "y")], 111 | list(x = "index", y = "value") 112 | ) 113 | 114 | p <- gg_subseries(tsbl_ped, Count, "day") 115 | 116 | expect_equal( 117 | ggplot2::layer_data(p)$y, 118 | tsbl_ped$Count[order((seq_along(tsbl_ped$Count) - 1)%%24)] 119 | ) 120 | expect_equivalent( 121 | as.numeric(table(ggplot2::layer_data(p)$PANEL)), 122 | rep(31, 24) 123 | ) 124 | 125 | p_built <- ggplot2::ggplot_build(p) 126 | 127 | expect_equivalent( 128 | p_built$plot$labels[c("x", "y")], 129 | list(x = "Date_Time", y = "Count") 130 | ) 131 | }) 132 | 133 | test_that("gg_lag() plots", { 134 | p <- gg_lag(tsbl_co2, value) 135 | 136 | expect_equal( 137 | ggplot2::layer_data(p, 2)$x, 138 | do.call(c, map(seq_len(9), function(i) 139 | tsbl_co2$value[seq_len(length(tsbl_co2$value) - i)])) 140 | ) 141 | expect_equivalent( 142 | as.numeric(table(ggplot2::layer_data(p, 2)$PANEL)), 143 | length(tsbl_co2$value) - seq_len(9) 144 | ) 145 | 146 | p_built <- ggplot2::ggplot_build(p) 147 | 148 | expect_equivalent( 149 | p_built$plot$labels[c("x", "y")], 150 | list(x = "lag(value, n)", y = "value") 151 | ) 152 | 153 | p <- gg_lag(tsbl_co2, value, lags = c(1, 4, 9)) 154 | 155 | expect_equal( 156 | ggplot2::layer_data(p, 2)$x, 157 | do.call(c, map(c(1, 4, 9), 158 | function(i) tsbl_co2$value[seq_len(length(tsbl_co2$value) - i)])) 159 | ) 160 | expect_equivalent( 161 | as.numeric(table(ggplot2::layer_data(p, 2)$PANEL)), 162 | length(tsbl_co2$value) - c(1, 4, 9) 163 | ) 164 | 165 | p_built <- ggplot2::ggplot_build(p) 166 | 167 | expect_equivalent( 168 | p_built$plot$labels[c("x", "y")], 169 | list(x = "lag(value, n)", y = "value") 170 | ) 171 | }) 172 | 173 | test_that("gg_tsdisplay() plots", { 174 | p <- gg_tsdisplay(tsbl_co2, value) 175 | 176 | expect_s3_class( 177 | p, "gg_tsensemble" 178 | ) 179 | 180 | expect_equal( 181 | ggplot2::layer_data(p[[1]], 1)$y, 182 | tsbl_co2$value 183 | ) 184 | 185 | p_built <- ggplot2::ggplot_build(p[[1]]) 186 | 187 | expect_equivalent( 188 | p_built$plot$labels[c("x", "y")], 189 | list(x = "index", y = "value") 190 | ) 191 | 192 | p <- p + ggplot2::labs(x = "x", y = "y", title = "title") 193 | 194 | p_built <- ggplot2::ggplot_build(p[[1]]) 195 | 196 | expect_equivalent( 197 | p_built$plot$labels[c("x", "y", "title")], 198 | list(x = "x", y = "y", title = "title") 199 | ) 200 | 201 | p_built <- ggplot2::ggplot_build(p[[2]]) 202 | 203 | expect_equivalent( 204 | p_built$plot$labels[c("x", "y")], 205 | list(x = "lag [1M]", y = "acf") 206 | ) 207 | 208 | p_built <- ggplot2::ggplot_build(p[[3]]) 209 | 210 | expect_equivalent( 211 | p_built$plot$labels[c("x", "y")], 212 | list(x = "index", y = "value") 213 | ) 214 | 215 | p <- gg_tsdisplay(tsbl_co2, value, plot_type = "histogram") 216 | 217 | p_built <- ggplot2::ggplot_build(p[[3]]) 218 | 219 | expect_equivalent( 220 | p_built$plot$labels[c("x", "y")], 221 | list(x = "value", y = "count") 222 | ) 223 | 224 | p <- gg_tsdisplay(tsbl_co2, value, plot_type = "scatter") 225 | 226 | expect_equal( 227 | ggplot2::layer_data(p[[3]], 1)$y, 228 | tsbl_co2$value[-1] 229 | ) 230 | 231 | p_built <- ggplot2::ggplot_build(p[[3]]) 232 | 233 | expect_equivalent( 234 | p_built$plot$labels[c("x", "y")], 235 | list(x = expression(Y[t - 1]), y = expression(Y[t])) 236 | ) 237 | 238 | p <- gg_tsdisplay(tsbl_co2, value, plot_type = "spectrum") 239 | 240 | p_built <- ggplot2::ggplot_build(p[[3]]) 241 | 242 | expect_equivalent( 243 | p_built$plot$labels[c("x", "y")], 244 | list(x = "frequency", y = "spectrum") 245 | ) 246 | }) 247 | 248 | test_that("gg_arma() plots", { 249 | skip_if_not_installed("fable") 250 | mdl <- tsbl_co2 %>% 251 | fabletools::model(fable::ARIMA(value ~ 0 + pdq(1,1,1) + PDQ(1,1,2))) 252 | 253 | p <- gg_arma(mdl) 254 | smmry <- fabletools::glance(mdl) 255 | ar_roots <- smmry$ar_roots[[1]] 256 | ma_roots <- smmry$ma_roots[[1]] 257 | 258 | expect_equal( 259 | ggplot2::layer_data(p, 4)$y, 260 | c(Im(1/ar_roots), Im(1/ma_roots)) 261 | ) 262 | expect_equal( 263 | ggplot2::layer_data(p, 4)$x, 264 | c(Re(1/ar_roots), Re(1/ma_roots)) 265 | ) 266 | expect_equal( 267 | ggplot2::layer_data(p, 4)$PANEL, 268 | factor(c(rep_along(ar_roots, 1), rep_along(ma_roots, 2))) 269 | ) 270 | 271 | p_built <- ggplot2::ggplot_build(p) 272 | 273 | expect_equivalent( 274 | p_built$plot$labels[c("x", "y")], 275 | list(x = "Re(1/root)", y = "Im(1/root)") 276 | ) 277 | }) 278 | -------------------------------------------------------------------------------- /tests/testthat/test-seats.R: -------------------------------------------------------------------------------- 1 | context("test-seats") 2 | 3 | skip_if(!is.null(safely(seasonal::checkX13)(fail = TRUE)$error)) 4 | skip_if(!.Platform$OS.type == "windows" && Sys.info()["sysname"] != "Linux") 5 | 6 | tsbl_co2 <- as_tsibble(co2) 7 | test_that("Bad inputs for seats decomposition", { 8 | expect_warning( 9 | tsibble::pedestrian %>% 10 | filter(Sensor == "Southern Cross Station", 11 | Date == as.Date("2015-01-01")) %>% 12 | model(feasts:::SEATS(Count)), 13 | "The X-13ARIMA-SEATS method only supports seasonal patterns" 14 | ) 15 | 16 | expect_warning( 17 | tsbl_co2 %>% 18 | model(feasts:::SEATS(value ~ seq_along(value))), 19 | "Exogenous regressors are not supported for X-13ARIMA-SEATS decompositions" 20 | ) 21 | 22 | expect_warning( 23 | tsbl_co2 %>% 24 | model(feasts:::SEATS(value, x11="")), 25 | "Use \\`X11\\(\\)\\` to perform an X11 decomposition" 26 | ) 27 | }) 28 | 29 | test_that("X-13ARIMA-SEATS decomposition", { 30 | dcmp <- tsbl_co2 %>% model(feasts:::SEATS(value)) %>% components() 31 | seas_dcmp <- seasonal::seas(co2) 32 | 33 | expect_equivalent( 34 | dcmp$trend, 35 | unclass(seas_dcmp$data[,"trend"]) 36 | ) 37 | expect_equivalent( 38 | dcmp$seasonal, 39 | unclass(seas_dcmp$data[,"adjustfac"]) 40 | ) 41 | expect_equivalent( 42 | dcmp$irregular, 43 | unclass(seas_dcmp$data[,"irregular"]) 44 | ) 45 | expect_equal( 46 | dcmp$value / dcmp$seasonal, 47 | dcmp$season_adjust 48 | ) 49 | }) 50 | -------------------------------------------------------------------------------- /tests/testthat/test-stl.R: -------------------------------------------------------------------------------- 1 | context("test-stl") 2 | 3 | test_that("Seasonal STL", { 4 | tsbl_uad <- as_tsibble(USAccDeaths) 5 | dcmp <- tsbl_uad %>% model(STL(value)) %>% components() 6 | stats_dcmp <- stats::stl(USAccDeaths, s.window = 11) 7 | 8 | expect_equivalent( 9 | dcmp$trend, 10 | unclass(stats_dcmp$time.series[,"trend"]) 11 | ) 12 | expect_equivalent( 13 | dcmp$season_year, 14 | unclass(stats_dcmp$time.series[,"seasonal"]) 15 | ) 16 | expect_equivalent( 17 | dcmp$remainder, 18 | unclass(stats_dcmp$time.series[,"remainder"]) 19 | ) 20 | expect_equal( 21 | dcmp$value - dcmp$season_year, 22 | dcmp$season_adjust 23 | ) 24 | }) 25 | 26 | 27 | test_that("Non-seasonal STL", { 28 | tsbl_www <- as_tsibble(WWWusage) 29 | dcmp <- tsbl_www %>% model(STL(value)) %>% components() 30 | stats_dcmp <- stats::supsmu(seq_along(WWWusage), WWWusage) 31 | 32 | expect_equivalent( 33 | dcmp$trend, 34 | stats_dcmp$y 35 | ) 36 | expect_equivalent( 37 | dcmp$remainder, 38 | unclass(WWWusage - stats_dcmp$y) 39 | ) 40 | expect_equal( 41 | dcmp$value, 42 | dcmp$season_adjust 43 | ) 44 | }) 45 | 46 | 47 | test_that("Multiple seasonality STL", { 48 | dt <- tsibble(idx = seq_len(100), 49 | y = rep(1:4, length.out = 100) + rep(1:7, length.out = 100), 50 | index = idx) 51 | dcmp <- dt %>% model(STL(y ~ season(4) + season(7))) %>% components() 52 | 53 | expect_equal( 54 | dcmp$trend, 55 | rep(6.5, 100), 56 | tolerance = 0.01 57 | ) 58 | expect_equal( 59 | dcmp$remainder, 60 | rep(0, 100), 61 | tolerance = 0.01 62 | ) 63 | expect_equal( 64 | dcmp$season_4, 65 | rep(1:4, length.out = 100) - 2.5, 66 | tolerance = 0.01 67 | ) 68 | expect_equal( 69 | dcmp$season_7, 70 | rep(1:7, length.out = 100) - 4, 71 | tolerance = 0.01 72 | ) 73 | expect_equal( 74 | dcmp$y - dcmp$season_4 - dcmp$season_7, 75 | dcmp$season_adjust 76 | ) %>% 77 | expect_equal( 78 | dcmp$trend + dcmp$remainder 79 | ) 80 | }) 81 | -------------------------------------------------------------------------------- /tests/testthat/test-x11.R: -------------------------------------------------------------------------------- 1 | context("test-x11") 2 | 3 | skip_if(!is.null(safely(seasonal::checkX13)(fail = TRUE)$error)) 4 | skip_if(!.Platform$OS.type == "windows" && Sys.info()["sysname"] != "Linux") 5 | 6 | test_that("Bad inputs for X11 decomposition", { 7 | expect_warning( 8 | tsibble::pedestrian %>% 9 | filter(Sensor == "Southern Cross Station", 10 | Date == as.Date("2015-01-01")) %>% 11 | model(feasts:::X11(Count)), 12 | "The X11 method only supports monthly" 13 | ) 14 | 15 | expect_warning( 16 | as_tsibble(co2) %>% 17 | model(feasts:::X11(value ~ seq_along(value))), 18 | "Exogenous regressors are not supported for X11 decompositions" 19 | ) 20 | }) 21 | 22 | test_that("Additive X11 decomposition", { 23 | tsbl_co2 <- as_tsibble(co2) 24 | dcmp <- tsbl_co2 %>% model(feasts:::X11(value)) %>% components() 25 | seas_dcmp <- seasonal::seas(co2, x11="", x11.mode = "add", 26 | transform.function = "none") 27 | 28 | expect_equivalent( 29 | dcmp$trend, 30 | unclass(seas_dcmp$data[,"trend"]) 31 | ) 32 | expect_equivalent( 33 | dcmp$seasonal, 34 | unclass(seas_dcmp$data[,"adjustfac"]) 35 | ) 36 | expect_equivalent( 37 | dcmp$irregular, 38 | unclass(seas_dcmp$data[,"irregular"]) 39 | ) 40 | expect_equal( 41 | dcmp$value - dcmp$seasonal, 42 | dcmp$season_adjust 43 | ) 44 | }) 45 | 46 | test_that("Multiplicative X11 decomposition", { 47 | tsbl_uad <- as_tsibble(USAccDeaths) 48 | dcmp <- tsbl_uad %>% model(feasts:::X11(value, type = "multiplicative")) %>% components() 49 | seas_dcmp <- seasonal::seas(USAccDeaths, x11="", x11.mode = "mult", 50 | transform.function = "log") 51 | 52 | expect_equivalent( 53 | dcmp$trend, 54 | unclass(seas_dcmp$data[,"trend"]) 55 | ) 56 | expect_equivalent( 57 | dcmp$seasonal, 58 | unclass(seas_dcmp$data[,"adjustfac"]) 59 | ) 60 | expect_equivalent( 61 | dcmp$irregular, 62 | unclass(seas_dcmp$data[,"irregular"]) 63 | ) 64 | expect_equal( 65 | dcmp$value / dcmp$seasonal, 66 | dcmp$season_adjust 67 | ) 68 | }) 69 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/feasts.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to feasts" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Introduction to feasts} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>", 14 | fig.height = 4, 15 | fig.width = 7 16 | ) 17 | ``` 18 | 19 | ```{r setup, message=FALSE} 20 | library(feasts) 21 | library(tsibble) 22 | library(dplyr) 23 | ``` 24 | 25 | The feasts package provides a smorgasbord of tools for analysing tidy temporal data in the tsibble format. The package name is an acronym of its primary functionality: *Feature Extraction and Statistics for Time Series*. 26 | 27 | Suppose we wanted to analyse seasonal patterns in the number of domestic travellers to Melbourne, Australia. In the `tsibble::tourism` data set, this can be further broken down into 4 reasons of travel: "business", "holiday", "visiting friends and relatives" and "other reasons". The first observation from each series are shown below. 28 | 29 | ```{r data} 30 | tourism_melb <- tourism %>% 31 | filter(Region == "Melbourne") 32 | tourism_melb %>% 33 | group_by(Purpose) %>% 34 | slice(1) 35 | ``` 36 | 37 | A useful first look at a time series is with a standard time series plot. 38 | 39 | ```{r plot} 40 | tourism_melb %>% 41 | autoplot(Trips) 42 | ``` 43 | 44 | This plot reveals that each of the reasons for visiting Melbourne follow a similar pattern, and that in recent years the amount of tourism is increasing. It is also clear that most people travel to Melbourne for one of three reasons (Business, Holiday or Visiting), and fewer travellers report other travel purposes. 45 | 46 | While this plot reveals many useful large scale features of the data, it can be difficult to identify specifics about the seasonal pattern (other than its existence). Which quarter has the most tourism travel in Melbourne? To find out we will need to investigate other plot types. 47 | 48 | ```{r season-plot} 49 | tourism_melb %>% 50 | gg_season(Trips) 51 | ``` 52 | 53 | The seasonal plot (`gg_season()`) wraps a seasonal period (in this case, years) over the x axis, allowing you to see how each quarter varies. In particular, it is apparent that Q3 is a low point for people visiting friends and relatives, which noticeably increases in Q4. Similarly Q2 and Q3 are the time periods with the most business travel. 54 | 55 | The trend of recent years can also be seen in the spread between the lines. The more recent years (purple/pink) are higher than the previous years. This also reveals that the drop in visiting tourism in Q3 was far less extreme in the most recent two years. 56 | 57 | ```{r subseries-plot} 58 | tourism_melb %>% 59 | gg_subseries(Trips) 60 | ``` 61 | 62 | An alternative visualisation of seasonal patterns is the subseries plot (`gg_subseries()`), which isolates seasonal periods into separate plots. The blue lines indicate the average number of trips in each quarter, making the increase in visiting tourism from Q3 to Q4 more obvious. This plot style is especially useful in seeing how seasonality changes over time. Focusing on the visiting tourism (last row of facets), the number of tourists in Q3 and Q4 are increasing much more than in Q1 and Q2 (suggesting that the trend may vary between seasons). 63 | 64 | A look at the correlations in each series could reveal structures which are difficult to identify in the above plots. 65 | 66 | ```{r acf} 67 | tourism_melb %>% 68 | ACF(Trips) 69 | ``` 70 | 71 | The above code computes autocorrelations (`ACF()`), however it is also possible to compute partial autocorrelations (`PACF()`) and cross-correlations (`CCF()`). 72 | 73 | The tables given from these correlation functions also have a nice `autoplot()` method, which will show the correlations along with a threshold for significance (controllable with the `level` argument). 74 | 75 | ```{r acf-plot} 76 | tourism_melb %>% 77 | ACF(Trips) %>% 78 | autoplot() 79 | ``` 80 | 81 | Another helpful strategy in investigating the patterns in a time series is to decompose it into components of interest. A useful decomposition for this is the STL decomposition, which allows you to extract multiple seasonal patterns with any seasonal period. 82 | 83 | ```{r stl} 84 | tourism_melb %>% 85 | model(STL(Trips ~ season(window = "periodic"))) %>% 86 | components() 87 | ``` 88 | 89 | The above call to `STL()` has decomposed the `Trips` variable into three components such that `Trips = trend + season_year + remainder`. By setting `season(window = "periodic")`, we have set the seasonal pattern to be unchanging --- you can control how quickly the seasonal pattern can change by setting this to some number (smaller numbers correspond to more rapid change). 90 | 91 | ```{r stl-plot} 92 | tourism_melb %>% 93 | model(STL(Trips ~ season(window = 9))) %>% 94 | components() %>% 95 | autoplot() 96 | ``` 97 | 98 | Much like the table from `ACF()`, decompositions can also be plotted with `autoplot()`. This gives the expected faceted plot of the components extracted from the measured variable. The plot shows that each purpose of travel has a different seasonal pattern, and that the strength and structure of this pattern has changed over time. As these components are often on different scales, this plot includes a set of scale bars which are of equal scaled size across all plots. 99 | 100 | The above plots and analysis are useful for if you're looking at a few series, but what can be done if you needed to look at and compare more time series? Extracting features from a collection of time series is a scalable approach to analysing many data sets. Each feature is a numerical summary of the data set's defining characteristics, and a set of features can be created using `feature_set()`. 101 | 102 | ```{r features} 103 | tourism_melb_features <- tourism_melb %>% 104 | features(Trips, feature_set(tags = "stl")) 105 | tourism_melb_features 106 | ``` 107 | 108 | In the example above, the components from the STL decomposition has been used to summarise the strength of trend and seasonality components of each series. 109 | 110 | These features are particularly useful to show on a plot. 111 | 112 | ```{r featutes-plot} 113 | library(ggplot2) 114 | tourism_melb_features %>% 115 | ggplot(aes(x = trend_strength, y = seasonal_strength_year, colour = Purpose)) + 116 | geom_point() + 117 | coord_equal() + 118 | lims(x = c(0,1), y = c(0,1)) 119 | ``` 120 | 121 | When analysing just four series, a plot of the features does not look very exciting. It is worth noting that a lot of the individuality seen in the previous analysis have been lost when each series is summarised down to just two values. However recall that the analysis has been working from a very small subset of the complete `tourism` data set. Let's see how Melbourne compares with the other regions in the data. 122 | 123 | ```{r features-all-plot} 124 | tourism_features <- tourism %>% 125 | features(Trips, feat_stl) 126 | 127 | ggplot(mapping = aes(x = trend_strength, y = seasonal_strength_year, colour = Purpose)) + 128 | geom_point(data = tourism_features, alpha = 0.3) + 129 | geom_point(data = tourism_melb_features, size = 2) + 130 | coord_equal() + 131 | facet_wrap(vars(Purpose)) + 132 | lims(x = c(0,1), y = c(0,1)) 133 | ``` 134 | 135 | Looks like Melbourne is one of the *trendiest* places around Australia! 136 | 137 | More information about time series analysis using the feasts package can be found in [*Forecasting: Principles and Practices (3rd Ed.)*](https://otexts.com/fpp3/) and in the [*pkgdown site*](https://feasts.tidyverts.org/). 138 | --------------------------------------------------------------------------------