5

I'm have issues with stl() time series decomposition function in R telling me my ts object is not univariate when it actually is?

tsData <- ts(data = dummyData, start = c(2012,1), end = c(2014,12), frequency = 12)

> tsData
     Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
2012  22  26  34  33  40  39  39  45  50  58  64  78
2013  51  60  80  80  93 100  96 108 111 119 140 164
2014 103 112 154 135 156 170 146 156 166 176 193 204

> class(tsData)
[1] "ts"

> stl(tsData, s.window = "periodic")
Error in stl(tsData, s.window = "periodic") : 
  only univariate series are allowed

> dput(dummyData)
structure(list(index = c(22L, 26L, 34L, 33L, 40L, 39L, 39L, 45L, 
50L, 58L, 64L, 78L, 51L, 60L, 80L, 80L, 93L, 100L, 96L, 108L, 
111L, 119L, 140L, 164L, 103L, 112L, 154L, 135L, 156L, 170L, 146L, 
156L, 166L, 176L, 193L, 204L)), .Names = "index", class = "data.frame", row.names = c(NA, 
-36L))

Anyone know how to fix this issue?

moku
  • 4,099
  • 5
  • 30
  • 52
  • I don't understand why you claim that `tsData` is univariate. It isn't. – Roland Jan 30 '15 at 21:50
  • What to you mean Roland its one variable that was converted to timeseries object – moku Jan 30 '15 at 21:55
  • Does this answer your question? [Time series and stl in R: Error only univariate series are allowed](https://stackoverflow.com/questions/10492155/time-series-and-stl-in-r-error-only-univariate-series-are-allowed) – Santiago Capobianco Dec 17 '20 at 18:51

7 Answers7

13

To avoid these kinds of problems or errors try to make a univariate time series just by forming the raw data points or values, calling ts() function.

Better speaking you should always put only the values of your variable not the whole structure of the variable. Let me explain it a little bit by a very simple example:

Imagine you have a variable X which is a vector (most probably imported or formed from the other data sources)by a 100x1 dimension, i.e. it contains 100 values or data points. If you want to make a univariate time series out of this vector the wrong way to do it is as like as you did for your case:

ts(X, frequency=24)

BE CAREFUL, the CORRECT way to do it is like this:

ts(X[1:100], frequency=24)

or even like this:

ts(X[1:100,1], frequency=24)

I hope my dear friend that you can avoid it for the next time you need to make a univariate time series..!!

Elias
  • 743
  • 7
  • 16
  • This is very interesting and helpful. But how do you create a `ts` from a column that you want to read from a `csv` file? So, my `X` is the (only) column of a file. I am a newbie to R. – FaCoffee Mar 22 '17 at 19:52
  • 1
    Usually you can use some of file reading commands in R (such as `read.csv`, `read.csv2` or even `read.delim`) to create a data frame from that file. Then you can use the numeric values or contents of the created data.frame (e.g by writing `X$x1[1:100]` if the first or the single column is named `x1`) to convert it to a univariate time series using `ts()` command. – Elias Mar 24 '17 at 14:13
  • 1
    Urgh. ts(X, frequency=24) certainly shouldn't be the 'wrong' way to do this. Poor design of ts. – Nick Apr 03 '20 at 16:54
5

I'm not 100% sure about what the exact cause of the problem is, but you can fix this by passing dummyData$index to ts instead of the entire object:

tsData2 <- ts(
  data=dummyData$index, 
  start = c(2012,1), 
  end = c(2014,12), 
  frequency = 12)
##
R>  stl(tsData2, s.window="periodic")
 Call:
 stl(x = tsData2, s.window = "periodic")

Components
            seasonal     trend   remainder
Jan 2012 -24.0219753  36.19189   9.8300831
Feb 2012 -20.2516062  37.82808   8.4235219
Mar 2012  -0.4812396  39.46428  -4.9830367
Apr 2012 -10.1034302  41.32047   1.7829612
May 2012   0.6077088  43.17666  -3.7843705
Jun 2012   4.4723800  45.22411 -10.6964877
Jul 2012  -7.6629462  47.27155  -0.6086074
Aug 2012  -1.0551286  49.50673  -3.4516016
Sep 2012   2.2193527  51.74191  -3.9612597
Oct 2012   7.3239448  55.27391  -4.5978509
Nov 2012  18.4285405  58.80591 -13.2344456
Dec 2012  30.5244146  63.70105 -16.2254684

...


I'm guessing that when you pass a data.frame to the data argument of ts, some extra attributes carry over, and although this generally doesn't seem to be an issue with many functions that take a ts class object (univariate or otherwise), apparently it is an issue for stl.

R>  all.equal(tsData2,tsData)
[1] "Attributes: < Names: 1 string mismatch >"                         
[2] "Attributes: < Length mismatch: comparison on first 2 components >"
[3] "Attributes: < Component 2: Numeric: lengths (3, 2) differ >"      
##
R>  str(tsData2)
 Time-Series [1:36] from 2012 to 2015: 22 26 34 33 40 39 39 45 50 58 ...
##
R>  str(tsData)
 'ts' int [1:36, 1] 22 26 34 33 40 39 39 45 50 58 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr "index"
 - attr(*, "tsp")= num [1:3] 2012 2015 12

Edit:

Looking into this a little further, I think the problem has to do with the dimnames attribute being carried over from the dummyData when it is passed as a whole. Note this excerpt from the body of stl:

if (is.matrix(x)) 
        stop("only univariate series are allowed")

and from the definition of matrix:

is.matrix returns TRUE if x is a vector and has a "dim" attribute of length 2) and FALSE otherwise

so although you are passing stl a univariate time series (the original tsData), as far as the function is concerned, a vector with a length 2 dimnames attribute (i.e. a matrix) is not a univariate series. It seems a little strange to do error handling in this way, but I'm sure the author of the function had a very good reason for this.

nrussell
  • 18,382
  • 4
  • 47
  • 60
2

Another way to fix the problem with the time series data is:

stl(tsData[, 1], s.window = "periodic")

Please compare

str(tsData)

with

str(tsData[, 1])

to see the difference.

guest
  • 21
  • 1
1

I had the same problem and this is how I fixed it:

stl(x[,1], s.window = "periodic")
  • Of all the fixes that I've come across, this has been the only one that has worked for me. Removing attributes didn't help. Taking a round trip from ts to vector and back didn't work. Even though obj obj[,1] is all TRUE. Go figure. – Richard Careaga Nov 30 '20 at 00:44
0

I had the same error and indeed, as mentioned by nrussell, the problem was that I was passing a time series that was also a matrix.

(However, the $index thing didn't work for me and R complained that a ts object must have one or more observations. I'm fairly new at R and have no idea why this is, but for me the following approach worked)

you can remedy this with:

dummyVector = as.vector(t(dummyData))

And then continue to get the stl:

tsData = ts(dummyVector, start=2012, end=(2014, 12), frequency = 12)
stl = stl(tsData, "periodic")

If you use R Studio, you can see that now, your timeseries is listed under

Time-Series [1:36] from 2012 to 2015: yourData

whereas before, it was likely listed as

int[1:3, 1:12] yourData

s-heins
  • 679
  • 1
  • 8
  • 20
0

Problem could be that you aren't specifying end = c(yyyy,mm).

If you are using decompose, you don't need to specify the end = . If you are switching to stl and using some old code, you'll need to add this parameter (if you weren't using it with decompose).

This fixed the "univariate" error issue for me.

jsm
  • 1
  • 1
0

When a data frame object is passed to ts() function it will be coerced to a numeric matrix via data.matrix() (see help(ts)). The ts() function will return a Time-Series object which will be a matrix of class 'ts', with a new attribute 'tsp', and the original data frame attributes. These lasts are converted into 'dimnames' as a list of 2 attributes: dimnames[[1]] (originally 'row.names') and dimnames[[2]] (originally 'names').

When a vector is passed to ts() it will return a Time-Series object composed of the vector and the 'tsp' attribute.

Finally, when a Time-Series object is passed to stl() it will check weather the object is a matrix via is-matrix() and will return an error if true.

if (is.matrix(x)) 
      stop("only univariate series are allowed")

Note:

  • A data frame is a list of vectors of equal length with attributes like 'names' and 'row.names'.
  • "A Time-Series object is a vector or matrix with class 'ts' and additional attributes" (help(ts)).

To check a R object attributes call the functions: attributes(obj) It's also possible to verify an object dimension, i.e, if it's a vector, with the functions is.vector(obj), dim(obj), and dimnames(obj).

See the code bellow for an example:

# Exploring stl() arguments #

# load packages ####
if (!require("pacman")) install.packages("pacman")
pacman::p_load("vctrs")
pacman::p_load("stats")

# create a data frame "df" ####
df <- vctrs::data_frame(x = 133:165, y = pi, z = seq(25.25, 1.25, by = -0.75))

# visualizing and checking attributes
head(df)
str(df)
attributes(df)
dimnames(df)

# accessing single vectors/variables with subsetting ####
# 1 - double brackets
head(df[[3]]) # return a vector
is.vector(df[[3]])
# 2 - $ operator
head(df$z) # returns a vector
is.vector(df$z)
# 3 - single brackets 
head(df[3]) # return a data frame!
is.vector(df[3])
dim(df[3])
dimnames(df[3])
attributes(df[3])

# creating a Time-Series object using ts() ####
MySeriesTs1 <- ts(df[[3]], start = c(1999, 10), frequency = 12)
MySeriesTs1
str(MySeriesTs1)
dim(MySeriesTs1) # time-series is a vector

MySeriesTs2 <- ts(df$z, start = c(1999, 10), frequency = 12)
MySeriesTs2
str(MySeriesTs2)
dim(MySeriesTs2) # time-series is a vector

MySeriesTs3 <- ts(df[3], start = c(1999, 10), frequency = 12)
MySeriesTs3
str(MySeriesTs3)
dim(MySeriesTs3) # time-series is a matrix!
dimnames(MySeriesTs3)
attributes(MySeriesTs3)

# Note: ts() accepts either a vector, a matrix or a data frame object as argument.
# here I pass the data frame with all three variables as argument to ts()
MySeriesTs <- ts(df, start = c(1999, 10), frequency = 12)
MySeriesTs
dim(MySeriesTs)
str(MySeriesTs) #returns a matrix with a List of 2 "dimnames" attributes: NULL and "x" "y" "z"

# calling stl() function ####
# stl() accepts only univariate time series as argument.
# Therefore, if the Time-Series object is a matrix, it has to be subset when passed to stl.
# passing a vector
MyDecompose1 <- stl(MySeriesTs1, s.window = 'p') # RUN
# passing a vector
MyDecompose2 <- stl(MySeriesTs2, s.window = 'p') # RUN
# passing a matrix
MyDecompose3 <- stl(MySeriesTs3, s.window = 'p') # ERROR!
# passing a subset of a matrix
MyDecompose3 <- stl(MySeriesTs3[,"z"], s.window = 'p') # RUN
# passing a matrix
MyDecompose <- stl(MySeriesTs, s.window = 'p') #ERROR!
# passing a subset of a matrix
MyDecompose <- stl(MySeriesTs[,"z"], s.window = 'p') #RUN