Derived time series
Sometimes it’s useful to compute time series based on the data in other
time series. For example, let’s say you have a series that measures
temperature in degrees Celsius. Let’s call it A. You can create a
derived time series that shows the same values converted to
Fahrenheit, by using this formula:
A * 1.8 + 32
The resulting derived time series can be graphed and exported just like any other time series.
The data points in a derived time series are computed on demand. This means that any changes to the data of the source series are instantly reflected in the derived series. It also means that data points in derived time series do not count towards your storage limit.
Introduction to 28times formulas
28times formulas are designed for making transformations to time series.
In the formula A * 1.8 + 32, the symbol A stands for the source time
series, consisting of temperature values paired with timestamps. Two
transformations are applied to this source time series:
A * 1.8results in a new ephemeral series with the same timestamps asA, but with each value multiplied by 1.8.- Addition is applied to the ephemeral series, resulting in a new time series, where each value is now in Fahrenheit.
Several other transformations are possible, here are some examples:
A - B: the values from seriesBare subtracted from the values in seriesA. The result is a new time series that holds the difference betweenAandBat each instant.sqrt(A): computes the square root of each value inA.from_earlier(A, 30 min): Results in a new time series with a new set of timestamps. Each instant in the resulting series receives the value from 30 minutes earlier in the source series. Their values are unchanged, but the timestamps are effectively shifted by 30 minutes towards the future. For example, infrom_earlier(A, 30 min), the value at the time 9:30 on a given date is the value at 9:00 inA. This function is useful for computing the change in value during a given time frame: The derived seriesA - from_earlier(A, 1 h)shows how much the metricAchanged during the last hour at any given point in time.
Source time series are referenced by a single uppercase letter: A,
B, C, and so on.
Operators
Five binary operators can be used in 28times formulas:
+(addition)-(subtraction)*(multiplication)/(division)^(exponentiation)
* and / bind tighter than + and -, and ^ binds tightest. For
example, A + B * C is equal to A + (B * C).
When a binary operator has a number on one side and a series on the other side, the result is a series with the same timestamps as the input series, but with the values transformed.
If a binary operator has series on both sides, the result is also a
series. For example, to compute A - B, each data point from A is
paired with a data point from B with the same timestamp. Any data
points without a corresponding timestamp in the other series remain
unused. Each value from time series B is then subtracted from the
corresponding value in time series A. The result is a new time series
with all timestamps that A and B have in common.
Unary - and + are also supported. They can be applied to numbers, as
in -sqrt(2), or to series, as in -(A + 3).
Functions
The following functions can be applied to numbers or series:
sqrt(…): The square root.log10(…): Base-10 logarithm.ln(…): Natural logarithm.
The following functions take a series as input and return a new series.
previous(…)
Each instant in the resulting series receives the value from the next
earlier data point in the source series. For example, A - previous(A)
computes the difference between each value and the one that comes before
it. previous(A) does not have a data point at the earliest time stamp
of A.
next(…)
Each instant in the resulting series receives the value from the next
later data point in the source series. next(A) does not have a data
point at the latest time stamp of A.
from_earlier(…, <duration>) and from_later(…, <duration>)
Look up values at an earlier/later instant. The first argument must be a time series. The result is a time series with a new set of timestamps.
<duration> is a combination of a number and a unit, for example
30 min or 1 h. The number must be an integer. Valid units are: s
(seconds), min (minutes), h (hours), d (days), w (weeks), mo
(months), y (years).
Note that these functions are named from the perspective of the
resulting time series: in the result of from_earlier, timestamps get
the value that is associated with an earlier timestamp in the input
series. In the result of from_later, timestamps get the value that is
associated with a later timestamp in the input series.
For example, a time series with value 1 at 8:00 and 2 at 9:00 on some
day would be the result of a from_earlier(A, 1 h) operation where A
has value 1 at 7:00 and 2 at 8:00.
For a duration measured in seconds, the resulting time series has
timestamps which are set apart from the input by exactly that number of
seconds. A duration 1 min is exactly the same as 60 s, and 1 h is
exactly the same as 60 min or 3600 s. Time zones don’t affect the
calculations for seconds, minutes, and hours, because the calculations
are done in terms of the actual number of seconds passed. This means
that jumps due to daylight saving time are correctly taken into account:
Two hours after 1:00 AM is usually 3:00 AM. But in some places, on rare
occasions, due to daylight saving time, two hours after 1:00 AM may be
2:00 AM or 4:00 AM.
When the time unit is days or longer, the resulting time series takes on
the value from the same time of day, but on a different date. For
example, the timestamp 2020-01-04 05:00 is one day after
2020-01-03 05:00. In most cases, this results in a difference of
exactly 24 hours, but on rare occasions, it does not. For example,
daylight saving time results in one day each year having 23 hours, and
another having 25 hours.
If the time zone of the derived series is UTC, a duration of one day is exactly the same as 24 hours.
A duration of one week is exactly the same as 7 days.
A duration of one or more months works similarly to a duration measured
in days: the day and time stay the same, but the month changes. The year
may change too, of course. For example, 3 months after
1999-12-31 00:00 is 2000-03-31 00:00.
A duration of one year is exactly the same as 12 months.
If – for a duration measured in days, weeks, months, or years – the
targeted timestamp doesn't exist, the last timestamp before the targeted
timestamp is used. For example, moving 2020-01-30 15:00 forward by one
month results in 2020-02-29 23:59:59, because there is no 30th of
February.
If you have data spread throughout the month, consider using a duration of 30 days instead of one month. This avoids discontinuities when the months don't have the same number of days. On the other hand, if data points are always associated with the first day of a month, a duration of one month is probably best.
bucket(…, <period>, <aggregation>)
The bucket() function is an experimental feature that may be changed
or replaced by a different function in the future.
The bucket function aggregates values from a time period. The first
argument must be a time series.
Possible periods are:
hourlydailyweekly_monweekly_tueweekly_wedweekly_thuweekly_friweekly_satweekly_sunmonthlyyearly
With bucket, all data points falling within a time period are
aggregated and the resulting value is associated with the timestamp at
the start of the period. Hourly periods result in timestamps with
minutes and seconds set to 0. For example, in a time series with data
points at 2020-03-14 08:15 and 2020-03-14 08:45, the resulting time
series with period hourly will aggregate the two values to a single
data point at 2020-03-14 08:00. With period daily, the resulting
data point is at 2020-03-14, which is equal to 2020-03-14 00:00.
Weekly periods result in timestamps at the start of the day of the week
in the period name. For example weekly_mon aggregates values from
Monday 00:00:00 to Sunday 23:59:59.
Monthly periods start on the first day of each month at midnight. Yearly periods start on January 1st at midnight.
Possible aggregations are:
mean: arithmetic mean of all valuesmedian: the median of all valuesmax: maximum, the highest valuemin: minimum, the lowest valuecount: number of valuesfirst: the earliest valuelast: the latest valuesum: the sum total
Constants
pi: the circle ratio π, 3.14159…e: Euler’s constant, 2.71828…
Details
Numeric values
All numeric values are IEEE-754 64-bit floating point numbers, also
known as doubles. Values resulting from a calculation can be
Infinity or NaN, for instance, as the result of a division by zero.
Number literals are decimal numbers. The following rules apply:
- A leading zero before a decimal point is optional:
0.5and.5are equivalent. - A decimal point and following zeros are optional:
1,1., and1.0are equivalent. - Leading zeros are allowed:
021is equivalent to21. - Scientific notation is allowed:
3e5and3.0e+5are equivalent to300000. - A literal that would overflow to
Infinityis an error.
Time zones
Mixing input series with different time zones in a formula is possible.
Each data point has an instant that is independent of the time zone. The
same instant has different textual timestamp representations in
different time zones. For example, 2020-01-30 20:00 in
America/New_York is equal to 2020-01-31 10:00 in Asia/Tokyo.
Conversely, the same textual timestamp representation may refer to
different points in time in different time zones.
If a derived time series has a formula using from_earlier(),
from_later(), or bucket(), the time calculations use the time zone
of that derived time series.
Order of operations
In the absence of parentheses, the operator precedence is as follows, with the highest precedence at the top.
(…)(parenthesized expressions) and<function>(…)(function calls such assqrt(…)).^(exponentiation) and unary operators-and+.- Binary operators
*and/. - Binary operators
+and-.
All binary operators except ^ are left-associative as usual:
A - B - C is equal to (A - B) - C. ^ is non-associative, meaning
that a chain of ^ operators, such as 3^4^5 is invalid and needs
mandatory parentheses to clarify the order of operations. A formula like
-3^2 is also invalid and needs clarifying parentheses: (-3)^2 or
-(3^2).
Parsing rules
Adjacent operators must be separated by a space. For example, A ^-2 is
not allowed, but A ^ -2 is. Adjacent unary operators are forbidden to
avoid mistakes: (- - A) is not allowed because it’s probably a typo.
It can be written as (-(-A)) or simply (A).