Why are TimeSeries objects reported with head "TemporalData"?



Related Problem: What does it mean to be an object in Mathematica?

I am quite surprised to find that

ts = TimeSeries[ {1,2,3,4}, {{1,2,3,4}}];
Head @ ts


(* TemporalData *)

I do find this quite inconsistent as the object is also reported with TimeSeries[...] when it is generated. I would find it much more sensible to have TemporalData-functions also accept TimeSeries objects but still have them as special instances with a "Head" of their own.

More formally: If TimeSeriesis a constructor then it should generate a TemporalDataobject and I should not see TimeSeries[...] at all after applying the function. On the other hand if it is a data type of its own then it should have its own head.

Am I missing something here? (e.g. are there good reasons for this behavior or can it be avoided)


Posted 2014-12-08T15:07:28.780

Reputation: 11 083

While every TimeSeries is a special case of TemporalData, the reverse is not true and thus by assigning the head "TemporalData" I am losing information which I now have to test for again (e.g. does the object only have one path?). – gwr – 2014-12-08T15:18:59.420

This is also true of EventSeries. Out of curiosity what information are you losing? You can always check for a single path with the "PathCount" property. There is also an undocumented "ObjectType" property which will tell you if it is a TimeSeries or generic TemnporalData object. – Andy Ross – 2014-12-08T16:32:08.777

@Andy Ross You are right about EventSeries, my fault. To me it seems much better to have every function that accepts TemporalData also accept a TimeSeries object. That is easy to implement and still gives me the Head as an identifier (so I do not need to dig into undocumented properties). The object is also generated as TimeSeries[...] why bother then at all with giving this object name? – gwr – 2014-12-08T16:44:24.880

Since TimeSeries does "construct" a TemporalData object there is of course not difficulty here. But if - as I have written - TimeSeries is merely a constructor function then it should not report the generation of a TimeSeries object (as it certainly does). I am no computer scientist but I cannot help to find this simply inconsistent. – gwr – 2014-12-08T16:57:29.390

1I absolutely agree. – Andy Ross – 2014-12-08T16:58:46.967

I am also no CS and have always found TimeSeries awkward, even though I haven't given much thought to why. But, as to your concrete problem, how can we help? Changing the formatting of a TemporalData that comes from a TimeSeries? Creating a TimeSeriesQ? – Rojo – 2014-12-09T11:52:11.910

I have always admired the scalability coming from the generality in Mma's deisng. I would propose two solutions: Either TimeSeries is better documented as a pure constructor function and there is no such result as TimeSeries[__] (yes, then something like TimeSeriesQ would make sense) or it is supported as an object of its own so that Head@ts returns TimeSeries and is made internally compatible with TemporalData-related functionality. – gwr – 2014-12-09T12:17:42.240



First Analysis

The official (first answer) from the technical support at Wolfram Research states that:

This seems consistent with the overall design of Mathematica where the head of an object that is a special case of some larger class generally has the head of the larger class (e.g. plots generally have the head Graphics).

[...] this may have some implications for bijectivity/invertibility [but should not be a problem] since most things in this area should have the head TemporalData.

While I agree that the problem seems limited it imho still is a design question which may be interesting in general since more and more we are talking about objects in Mathematica where there used to be atoms and normal expressions only.

I do not quite follow the logic with regard to the Plot example:

g = Plot[x^2, {x,-2,2}];
Head @ g

(* Graphics *)

will give the more general head indeed but here we are talking about a constructor. (Please correct me since my knowledge about computer science stems from reading Roman Maeder's Computer Science with Mathematica ). And naturally we are interested in the information of the object (data type) that has been constructed.

Quite in accordance here we never again see reference to the constructor again:

(OwnValues @ g)[[1,2]]
(* shows a Graphics object *)

Also using FullForm @ g will show that there is not a trace left indicating the Graphics-Object to be a special case 'Plot' (rather than say a BarChart).

This is different for TimeSeries:

While at first the analogy holds

ts = TimeSeries[ {1,2,3,4}, {{ 1,2,3,4 }} ];
Head @ ts

(* TemporalData *)

It is different further down the road:

(OwnValues @ ts)[[1,2]]
(* shows TimeSeries[ ... ] *)

and referencing FullForm @ ts clearly shows that we are talking about a TemporalData object of subclass (?) TimeSeries, since

(FullForm @ ts)[[1,1]]
(* TimeSeries *)

Note: As has been indicated by Andy Ross, the undocumented ts["ObjectType"] will serve as a TimeSeriesQ function.

But here again I do get the impression that deeper design changes are at work:

expr = h[ e1, e2, e3, e4 ]; expr[[1]]

(* e1 *)

Compare this Mathematica 1.0.1 -like behavior to:

f = FullForm @ ts

(* TemporalData[TimeSeries, List[ ... ], False, 10.'] *)   

which surprises when asked about its first part:


(* returns more than simple *TimeSeries* , e.g. full information about 
number of data and times *)

So it seems that object is not equal to object?

Proposal for further releases

I am not computer scientist enough to follow through the design implications at hand here but while I understood the Mathematica-design described by Roman Maeder I am struggling with some design changes in the latest releases of Mathematica.

Possible Route 1

Everything is kept as is but the documentation is at least made a bit more enlightening and maybe a TimeSeriesQ and EventSeriesQ is implemented for special instances.

Possible Route 2

TimeSeries is seen as a mere constructor and thus a (special case) of 'TemporalData[...]' is reported as the result (analog to Plot and the like for Graphics). Thus OwnValues would not show TimeSeries but TemporalData.

Possible Route 3 (is there one?)

Thinking and reading up about classes and subclasses I believe that subclasses extend classes they are special cases of but are referenced as a class in their own right. Maybe (using upvalues in Mma) one wants to tie separate methods to a TimeSeries - object while at the same time allowing for use of TimeSeries objects whenever a TemporalData object is asked for (e.g. f[td_TemporalData] might also accept a TimeSeries).

So this would mean leaving TimeSeries[...] as a FullForm Representation while changing the interpretation of Head (?) with regard to objects?


Not being savvy enough in this regard (but nontheless interested) I would see the first proposal as a minimal solution (for clarity) while prefering the 2nd route.


Posted 2014-12-08T15:07:28.780

Reputation: 11 083

Seems that nobody has come up with another answer or cares, thus I accept my own thoughts reflecting upon the answer I have gotten from WRI. – gwr – 2014-12-13T13:55:23.413

3I've come across the same issue with QuantityArray, which formats as though it still has head QuantityArray but really it's a StructuredArray. It's a really unfortunate design decision in my opinion. But just like the quantity case, there is a Q function for testing - TemporalData`TimeSeriesQ and QuantityArray`QuantityArrayQ – Jason B. – 2018-07-31T14:10:37.513