/R/tsibble2ts.R

https://github.com/tidyverts/tsibble · R · 216 lines · 138 code · 17 blank · 61 comment · 29 complexity · d7a6d1df0a0b7b7e50b90397ecaca349 MD5 · raw file

  1. #' Coerce a tsibble to a time series
  2. #'
  3. #' \lifecycle{stable}
  4. #'
  5. #' @param x A `tbl_ts` object.
  6. #' @param value A measured variable of interest to be spread over columns, if
  7. #' multiple measures.
  8. #' @param frequency A smart frequency with the default `NULL`. If set, the
  9. #' preferred frequency is passed to `ts()`.
  10. #' @param fill A value to replace missing values.
  11. #' @param ... Ignored for the function.
  12. #'
  13. #' @return A `ts` object.
  14. #' @export
  15. #'
  16. #' @examples
  17. #' # a monthly series
  18. #' x1 <- as_tsibble(AirPassengers)
  19. #' as.ts(x1)
  20. as.ts.tbl_ts <- function(x, value, frequency = NULL, fill = NA_real_, ...) {
  21. stopifnot(!is_null(fill))
  22. value <- enquo(value)
  23. key_vars <- key(x)
  24. if (length(key_vars) > 1) {
  25. abort("Can't proceed with the key of multiple variables.")
  26. }
  27. mvars <- measured_vars(x)
  28. str_val <- comma(backticks(mvars))
  29. if (quo_is_missing(value)) {
  30. if (is_false(has_length(mvars, 1) || is_empty(key_vars))) {
  31. abort(sprintf("Can't determine column `value`: %s.", str_val))
  32. }
  33. value_var <- mvars
  34. } else {
  35. value_var <- vars_pull(names(x), !!value)
  36. if (is_false(value_var %in% mvars)) {
  37. abort(sprintf("Column `value` must be one of them: %s.", str_val))
  38. }
  39. }
  40. idx <- index(x)
  41. vars_fill <- vec_rep(fill, length(value_var))
  42. vars_fill <- set_names(vars_fill, nm = value_var)
  43. tsbl_sel <- fill_gaps(
  44. select(x, !!idx, !!!key_vars, !!value_var),
  45. !!!vars_fill, .full = TRUE)
  46. pivot_wider_ts(tsbl_sel, frequency = frequency)
  47. }
  48. pivot_wider_ts <- function(data, frequency = NULL) {
  49. index <- index_var(data)
  50. df_rows <- data[[index]]
  51. idx_time <- time(df_rows)
  52. rows <- vec_unique(df_rows)
  53. key_rows <- key_rows(data)
  54. mvars <- measured_vars(data)
  55. if (has_length(mvars, 1)) {
  56. res <- data[[mvars]]
  57. } else {
  58. res <- data[mvars]
  59. }
  60. if (!is_empty(key_vars(data))) {
  61. res <- matrix(res, ncol = vec_size(key_rows))
  62. colnames(res) <- vec_unique(data[[key_vars(data)]])
  63. }
  64. if (is_null(frequency)) {
  65. frequency <- frequency(idx_time)
  66. }
  67. ts(res, start(idx_time), frequency = frequency)
  68. }
  69. #' @export
  70. time.yearweek <- function(x, ...) {
  71. freq <- guess_frequency(x)
  72. y <- decimal_date(x)
  73. ts(y, start = min0(y), frequency = freq)
  74. }
  75. #' @export
  76. time.yearmonth <- function(x, ...) {
  77. freq <- guess_frequency(x)
  78. y <- year(x) + (month(x) - 1) / freq
  79. ts(y, start = min0(y), frequency = freq)
  80. }
  81. #' @export
  82. time.yearquarter <- function(x, ...) {
  83. freq <- guess_frequency(x)
  84. y <- year(x) + (quarter(x) - 1) / freq
  85. ts(y, start = min0(y), frequency = freq)
  86. }
  87. #' @export
  88. time.numeric <- function(x, ...) {
  89. ts(x, start = min0(x), frequency = 1)
  90. }
  91. #' @export
  92. time.Date <- function(x, frequency = NULL, ...) {
  93. if (is.null(frequency)) {
  94. frequency <- guess_frequency(x)
  95. }
  96. y <- decimal_date(x)
  97. ts(x, start = min0(y), frequency = frequency)
  98. }
  99. #' @export
  100. time.POSIXt <- function(x, frequency = NULL, ...) {
  101. if (is.null(frequency)) {
  102. frequency <- guess_frequency(x)
  103. }
  104. y <- decimal_date(x)
  105. ts(x, start = min0(y), frequency = frequency)
  106. }
  107. #' Guess a time frequency from other index objects
  108. #'
  109. #' @description
  110. #' \lifecycle{stable}
  111. #'
  112. #' A possible frequency passed to the `ts()` function
  113. #'
  114. #' @param x An index object including "yearmonth", "yearquarter", "Date" and others.
  115. #'
  116. #' @details If a series of observations are collected more frequently than
  117. #' weekly, it is more likely to have multiple seasonalities. This function
  118. #' returns a frequency value at its smallest. For example, hourly data would
  119. #' have daily, weekly and annual frequencies of 24, 168 and 8766 respectively,
  120. #' and hence it gives 24.
  121. #'
  122. #' @references <https://robjhyndman.com/hyndsight/seasonal-periods/>
  123. #'
  124. #' @export
  125. #'
  126. #' @examples
  127. #' guess_frequency(yearquarter("2016 Q1") + 0:7)
  128. #' guess_frequency(yearmonth("2016 Jan") + 0:23)
  129. #' guess_frequency(seq(as.Date("2017-01-01"), as.Date("2017-01-31"), by = 1))
  130. #' guess_frequency(seq(
  131. #' as.POSIXct("2017-01-01 00:00"), as.POSIXct("2017-01-10 23:00"),
  132. #' by = "1 hour"
  133. #' ))
  134. guess_frequency <- function(x) {
  135. UseMethod("guess_frequency")
  136. }
  137. #' @export
  138. guess_frequency.numeric <- function(x) {
  139. if (has_length(x, 1)) {
  140. 1
  141. } else {
  142. gcd_interval(x)
  143. }
  144. }
  145. #' @export
  146. guess_frequency.yearweek <- function(x) {
  147. if (has_length(x, 1)) {
  148. 52.18
  149. } else {
  150. round(365.25 / 7 / interval_pull(x)$week, 2)
  151. }
  152. }
  153. #' @export
  154. guess_frequency.yearmonth <- function(x) {
  155. if (has_length(x, 1)) {
  156. 12
  157. } else {
  158. 12 / interval_pull(x)$month
  159. }
  160. }
  161. #' @export
  162. guess_frequency.yearmon <- guess_frequency.yearmonth
  163. #' @export
  164. guess_frequency.yearquarter <- function(x) {
  165. if (has_length(x, 1)) {
  166. 4
  167. } else {
  168. 4 / interval_pull(x)$quarter
  169. }
  170. }
  171. #' @export
  172. guess_frequency.yearqtr <- guess_frequency.yearquarter
  173. #' @export
  174. guess_frequency.Date <- function(x) {
  175. if (has_length(x, 1)) {
  176. 7
  177. } else {
  178. 7 / interval_pull(x)$day
  179. }
  180. }
  181. #' @export
  182. guess_frequency.POSIXt <- function(x) {
  183. int <- interval_pull(x)
  184. number <- int$hour + int$minute / 60 + int$second / 3600
  185. if (has_length(x, 1)) {
  186. 1
  187. } else if (number > 1 / 60) {
  188. 24 / number
  189. } else if (number > 1 / 3600 && number <= 1 / 60) {
  190. 3600 * number
  191. } else {
  192. 3600 * 60 * number
  193. }
  194. }
  195. #' @export
  196. frequency.tbl_ts <- function(x, ...) {
  197. abort_if_irregular(x)
  198. guess_frequency(x[[index_var(x)]])
  199. }