Building models on Timetric¶
Timetric allows you to create new series based on existing series in the platform. This lets you build models: both on data, and on the models built by other users.
Every model you build requires two pieces of information:
- A formula specifying how the model is built.
- A list of series used as input (which we call its parent series.)
There are two sorts of models you can build on Timetric — history-independent and history-dependent models.
History-independent models
The first sort of model is where the value of a model at any given timestamp depends only on the values of that model’s parent series at the same timestamp. In particular, the current value of the model depends only on the current values of its parents - the same behaviour formulae have in spreadsheets.
This is the sort of model used in the Build on these series feature on the website.
Let’s take an example: for instance, let’s say you want to work out the total GDP of all the EU countries. To do that, you would sum up the current value of the GDP of each country: as you moved back in time, the sum of the GDP of each of the countries at that point would give you the total GDP of the EU as of that moment in time. The end result of the mode is, therefore, a historical record of the total GDP, calculated from the historical records of the GDP for each country.
The formula for this would simply be = SUM(GDP_UK, GDP_FR, GDP_DE, ...).
History-dependent models
The other sort of model is for those where the history of a parent series does enter into the model. In this case, the value of the model depends not only on the current value of the parent series, but also on its values in the past. A simplest example of this is applying a smoothing function to a noisy data series; these models are those used by the “Filter” functionality of the website.
For example: you have daily exchange rate data, but you want to do some averaging to smooth out short-term fluctuations. In this case, you would want to calculate a running mean — say, over ten points (ten points being two business weeks).
In this case, the latest value of the model depends not only on the latest value of the underlying data, but on its values over the last two weeks: we call this a history-dependent model.
History-independent calculations¶
As we mentioned earlier, history-independent calculations focus on the current value of each series. The latest value of this sort of calculation is the result of doing the calculation on the latest value of its parent series. The timeseries that results from a history-independent calculation is the one you’d get if you performed the calculation at every point in the history of each of the model’s parent series.
The formula is specified in the same way as if you were creating a calculated series interactively through the website. The formula language is very much like that used by Excel. Full documentation is available elsewhere.
The formula is specified as a string. For example, you can create a formula looking like this:
= SQRT(x + y)
When you set up calculations through the website, the variable names to put in the formula are automatically created for you. However, when creating them through the API, you need to choose your own variable names and map these to the parent series. We call this the parentmap, and it is given as a JSON dictionary. For example, for the above the parentmap might be:
{"x": "http://timetric.com/series/RyXK2H6KQcOVmtCwOfwU2Q/",
"y": "http://timetric.com/series/DMrAf_pQRj6jeItUKxpXGQ/"}
Note
Variable names must consist only of ASCII alphanumeric characters, and must not begin with a number. They are treated case-insensitively.
The parent series are specified with URLs. However, only URLs for series on Timetric are permitted.
A calculated series is created in the same way as a data series — by POSTing to the series creation URL:
/create/
The same metadata — title and so forth — can be used, but instead of posting data (whether as value, timestamp, or csv), you need to post formula and parentmap parameters. So, to post the example shown above, you’d create the parameter dictionary as follows. (Note that the parentmap JSON dictionary must be escaped):
{"title": "Formula",
"caption": "Simple formula",
"formula": "= SQRT(x + y)"
"parentmap": "{\"x\": \"http://timetric.com/series/RyXK2H6KQcOVmtCwOfwU2Q/\",
\"y\": \"http://timetric.com/series/DMrAf_pQRj6jeItUKxpXGQ/\"}"
}
That translates to an HTTP request like:
https://timetric.com/create/
POST /create/ HTTP/1.1
Host: timetric.com
Content-Type: application/x-www-form-urlencoded
title=Formula&caption=Simple%20formula&formula=%3D%20SQRT%28x%20%2By%29&parentmap=%7B%22x%22%3A%22http%3A%2F%2Ftimetric.com%2Fseries%2FRyXK2H6KQcOVmtCwOfwU2Q%2F%22%2C%22y%22%3A%22http%22%2F%2Ftimetric.com%2Fseries%2FDMrAf_pQRj6jeItUKxpXGQ%2F%22%7D
and the expected HTTP response code is 201.
History-dependent calculations¶
For history-dependent calculations, the current value of the result depends on more than just the current value of its parent — instead, it depends on that parent’s history.
For these calculations, there can only ever be one parent. Therefore, the specified parentmap must only have one member, and to avoid errors its key must be the empty string.
An example parentmap is:
{"": "http://timetric.com/series/RyXK2H6KQcOVmtCwOfwU2Q/"}
Additionally, the formula language is much more restrictive. A history-dependent formula consists of two parts, called a Mapper and a Reducer. (See below for an explanation of these terms.)
A filter formula must begin with an exclamation mark, followed by the Reducer function, followed by the Mapper function (with any parameters it takes).
For example:
! Mean(WindowByPoint(num_points=10))
These are created in exactly the same way as history-independent series — by POSTing parameters to /create/. In this case, the parameters above would look like this (again, escaping the JSON dictionary):
{"title": "Filter",
"caption": "Simple filter",
"formula": "! Mean(WindowByPoint(10))"
"parentmap": "{\"\": \"http://timetric.com/series/RyXK2H6KQcOVmtCwOfwU2Q/\"}"
}
which translates to an HTTP request like this:
https://timetric.com/create/
POST /create/ HTTP/1.1
Host: timetric.com
Content-Type: application/x-www-form-urlencoded
title=Filter&caption=Simple%20filter&formula=%21%20Mean%28WindowByPoint%2810%29%29&parentmap=%7B%22%22%3A%22http%3A%2F%2Ftimetric.com%2Fseries%2FRyXK2H6KQcOVmtCwOfwU2Q%2F%22%7D
Once again, the expected HTTP response code is 201.
Filter formulae¶
The filter formula language is based around two sets of functions which you need to combine: Mapper functions and Reducer functions. You need one of each for a filter. You don’t need to worry too much about how they work beneath the surface — most formulae can be read easily:
! Mean(WindowByPoint(num_points=10))
corresponds to a 10-point moving mean of a series.
Note
These are so called because they work first by Mapping a timeseries into a series of portions, each of which is then Reduced to a single point; the result of the filter is one point for each Mapper.
For example, when you do Mean(WindowByPoint(num_points=10)), the Mapper: WindowByPoint is invoked across the whole series, with a parameter numpoints=10. This divides the series into every possible continuous length-10 chunk; these chunks may overlap, and points in the series may — indeed, probably will — appear in multiple chunks. Each of those chunks is then fed to the Reducer, Mean`, and their mean is taken. The result is a new series with one point for each output of the mapper.
Both Reducer and Mapper functions are case-insensitive.
Reducer functions¶
The reducer functions determine the basic mathematical operation you’re interested in performing. The most common choice is Mean, used for averaging a series.
- Mean: Arithmetic mean of input
- Geometric_Mean: Geometric mean of input
- Sum: Sum of input
- Difference: Difference between first and last points of input
Mapper functions¶
Using different mapper functions, you can specify which portions of the history of the series determine the value of each point in the model (in other words, which the Reducer is to operate on). In the first instance, though, you will probably only be interested in the most basic functions:
WindowByPoint
This takes one argument; a number of points; and applies the reducer to that many points.
So, to do a 10-point running average, you’d use the Mean reducer, with the WindowByPoint mapper, asking for 10 points:
! Mean(WindowByPoint(num_points=10))
WindowByTime
This takes a length of time, and units of time, and applies the reducer to all points within that range of time.
So, to do a running one-hour geometric mean of a series, you’d use the GeometricMean reducer, with the WindowByTime mapper, asking for either 1 hour or 3600 seconds:
! Geometric_Mean(WindowByTime(interval=3600, timeunit=second)) ! Geometric_Mean(WindowByTime(interval=1, timeunit=hour))