diff --git a/NAMESPACE b/NAMESPACE index 30d3736e..f65fd458 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ S3method(tinyplot,default) S3method(tinyplot,density) S3method(tinyplot,formula) +S3method(tinyplot,ts) export(draw_legend) export(get_saved_par) export(plt) @@ -122,6 +123,7 @@ importFrom(stats,quantile) importFrom(stats,setNames) importFrom(stats,spline) importFrom(stats,terms) +importFrom(stats,time) importFrom(stats,weighted.mean) importFrom(tools,file_ext) importFrom(utils,globalVariables) diff --git a/NEWS.md b/NEWS.md index 49a7d229..c173597c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,13 @@ where the formatting is also better._ ## Development version +### New features + +- New dedicated `tinyplot()` method for `ts` time series. Internally, this sets + up a long data frame with columns `Time`, `Value`, and `Series` and then calls + the formula method with different possible specifications for the `by` and `facet` + variables. (#558 @zeileis) + ### Aesthetic changes - The legend plot characters for the `"pointrange"` and `"errorbar"` types now diff --git a/R/tinyplot.ts.R b/R/tinyplot.ts.R new file mode 100644 index 00000000..7576049a --- /dev/null +++ b/R/tinyplot.ts.R @@ -0,0 +1,70 @@ +#' tinyplot Method for Plotting ts Objects (Time Series) +#' +#' @description Convenience interface for visualizing ts objects +#' (time sereis with tinyplot. +#' +#' @details Internally the time series object is converted to a long +#' data frame with columns `Time` (time index), `Value` (observations), +#' and `Series` (factor with column labels). Depending on the settings +#' of `facet` this data frame is visualized either with the formula +#' `Value ~ Time` or `Value ~ Time | Series`. See the `facet` argument +#' description for more details and the examples for some illustrations. +#' +#' @param x an object of class `"ts"`. +#' @param facet specification of `facet` for `tinyplot.formula`. The +#' default in the `tinyplot` method is to use `facet = NULL` for univariate +#' series and `facet = ~ Series` (equivalent to `facet = "by"`) for multivariate series. +#' @param type,facet.args,ylab,... further arguments passed to `tinyplot`. +#' +#' @examples +#' tinytheme("clean2") +#' +#' ## univariate series +#' tinyplot(Nile) +#' +#' ## multivariate +#' tinyplot(EuStockMarkets) ## multiple, same color, free scales +#' tinyplot(EuStockMarkets, facet.args = NULL) ## multiple, same color, same scale +#' tinyplot(EuStockMarkets, facet = "by") ## multiple, separate colors, free scales +#' tinyplot(EuStockMarkets, facet = NULL) ## single, separate colors +#' +#' ## further variations +#' tinyplot(EuStockMarkets, facet = "by", facet.args = NULL) +#' tinyplot(EuStockMarkets, facet.args = list(free = TRUE, ncol = 1)) +#' +#' tinytheme() ## reset +#' +#' @importFrom stats time +#' @export +tinyplot.ts = function(x, facet, type = "l", facet.args = list(free = TRUE), ylab = "", ...) { + ## basic object properties + n = NROW(x) + k = NCOL(x) + lab = deparse(substitute(x)) + if (k > 1L) lab = paste(lab, 1L:k, sep = ".") + if (!is.null(colnames(x))) lab = colnames(x) + + ## convert to long data.frame + df = data.frame( + Time = rep.int(as.numeric(time(x)), k), + Value = as.numeric(x), + Series = factor(rep(1L:k, each = n), labels = lab) + ) + + ## default for facet + single = k == 1L + if(missing(facet)) { + auto = TRUE + facet = if(single) NULL else ~ Series + } else { + auto = FALSE + } + if (is.null(facet)) facet.args = NULL + + ## call tinyplot + if(single | (!is.null(facet) & auto)) { + tinyplot(Value ~ Time, data = df, facet = facet, facet.args = facet.args, type = type, ylab = ylab, ...) + } else { + tinyplot(Value ~ Time | Series, data = df, facet = facet, facet.args = facet.args, type = type, ylab = ylab, ...) + } +} diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-by-free.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-by-free.svg new file mode 100644 index 00000000..7f761062 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-by-free.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + +Series +Series 1 +Series 2 +Series 3 +Series 4 +Series 5 + + + + + + + +Time + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +-2 +0 +2 +4 + +Series 1 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-10 +-8 +-6 +-4 +-2 +0 + +Series 2 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-2 +0 +2 +4 +6 +8 + +Series 3 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +-2 +0 +2 +4 + +Series 4 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + + +-6 +-4 +-2 +0 +2 +4 +6 + +Series 5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-by-same.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-by-same.svg new file mode 100644 index 00000000..25240471 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-by-same.svg @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + +Series +Series 1 +Series 2 +Series 3 +Series 4 +Series 5 + + + + + + + +Time + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 1 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 2 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 3 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 4 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-column.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-column.svg new file mode 100644 index 00000000..87b988f3 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-column.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + +Time + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +0 +4 + +Series 1 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-10 +-4 +0 + +Series 2 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-2 +2 +6 + +Series 3 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +0 +4 + +Series 4 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + + +-6 +0 +4 + +Series 5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-free.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-free.svg new file mode 100644 index 00000000..88b7b622 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-free.svg @@ -0,0 +1,242 @@ + + + + + + + + + + + + + +Time + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +-2 +0 +2 +4 + +Series 1 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-10 +-8 +-6 +-4 +-2 +0 + +Series 2 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + +-2 +0 +2 +4 +6 +8 + +Series 3 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +-2 +0 +2 +4 + +Series 4 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + + + +-6 +-4 +-2 +0 +2 +4 +6 + +Series 5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-same.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-same.svg new file mode 100644 index 00000000..b70592c3 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-same.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + +Time + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 1 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 2 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 3 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 4 + + + + + + + + + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + +Series 5 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-multivariate-single.svg b/inst/tinytest/_tinysnapshot/ts-multivariate-single.svg new file mode 100644 index 00000000..6bc4d24c --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-multivariate-single.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + +Series +Series 1 +Series 2 +Series 3 +Series 4 +Series 5 + + + + + + + +Time + + + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + +-10 +-5 +0 +5 + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/ts-univariate.svg b/inst/tinytest/_tinysnapshot/ts-univariate.svg new file mode 100644 index 00000000..1266463e --- /dev/null +++ b/inst/tinytest/_tinysnapshot/ts-univariate.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + +Time + + + + + + + +0 +20 +40 +60 +80 +100 + + + + + + +-4 +-2 +0 +2 +4 + + + + + + + + + + + + diff --git a/inst/tinytest/test-ts.R b/inst/tinytest/test-ts.R new file mode 100644 index 00000000..39943745 --- /dev/null +++ b/inst/tinytest/test-ts.R @@ -0,0 +1,48 @@ +source("helpers.R") +using("tinysnapshot") + +## five random walks +set.seed(0) +x = c(0, rnorm(100)) |> + cumsum() |> + replicate(n = 5) |> + ts(start = 0) + +## univariate + +f = function() { + tinyplot(x[, 1]) +} +expect_snapshot_plot(f, label = "ts-univariate") + +## multivariate + +f = function() { + tinyplot(x) +} +expect_snapshot_plot(f, label = "ts-multivariate-free") + +f = function() { + tinyplot(x, facet.args = NULL) +} +expect_snapshot_plot(f, label = "ts-multivariate-same") + +f = function() { + tinyplot(x, facet = "by") +} +expect_snapshot_plot(f, label = "ts-multivariate-by-free") + +f = function() { + tinyplot(x, facet = "by", facet.args = NULL) +} +expect_snapshot_plot(f, label = "ts-multivariate-by-same") + +f = function() { + tinyplot(x, facet = NULL) +} +expect_snapshot_plot(f, label = "ts-multivariate-single") + +f = function() { + tinyplot(x, facet.args = list(free = TRUE, ncol = 1)) +} +expect_snapshot_plot(f, label = "ts-multivariate-column") diff --git a/man/tinyplot.ts.Rd b/man/tinyplot.ts.Rd new file mode 100644 index 00000000..752897f8 --- /dev/null +++ b/man/tinyplot.ts.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tinyplot.ts.R +\name{tinyplot.ts} +\alias{tinyplot.ts} +\title{tinyplot Method for Plotting ts Objects (Time Series)} +\usage{ +\method{tinyplot}{ts}(x, facet, type = "l", facet.args = list(free = TRUE), ylab = "", ...) +} +\arguments{ +\item{x}{an object of class \code{"ts"}.} + +\item{facet}{specification of \code{facet} for \code{tinyplot.formula}. The +default in the \code{tinyplot} method is to use \code{facet = NULL} for univariate +series and \code{facet = ~ Series} (equivalent to \code{facet = "by"}) for multivariate series.} + +\item{type, facet.args, ylab, ...}{further arguments passed to \code{tinyplot}.} +} +\description{ +Convenience interface for visualizing ts objects +(time sereis with tinyplot. +} +\details{ +Internally the time series object is converted to a long +data frame with columns \code{Time} (time index), \code{Value} (observations), +and \code{Series} (factor with column labels). Depending on the settings +of \code{facet} this data frame is visualized either with the formula +\code{Value ~ Time} or \code{Value ~ Time | Series}. See the \code{facet} argument +description for more details and the examples for some illustrations. +} +\examples{ +tinytheme("clean2") + +## univariate series +tinyplot(Nile) + +## multivariate +tinyplot(EuStockMarkets) ## multiple, same color, free scales +tinyplot(EuStockMarkets, facet.args = NULL) ## multiple, same color, same scale +tinyplot(EuStockMarkets, facet = "by") ## multiple, separate colors, free scales +tinyplot(EuStockMarkets, facet = NULL) ## single, separate colors + +## further variations +tinyplot(EuStockMarkets, facet = "by", facet.args = NULL) +tinyplot(EuStockMarkets, facet.args = list(free = TRUE, ncol = 1)) + +tinytheme() ## reset + +}