<< Click to Display Table of Contents >> Timeseries |
Note: There is an additional kind of timeseries available in Gekko, namely so-called array-timeseries. These are explained in a later section. |
Timeseries variables can be defined and transformed easily, and variables from any open databank can be used in a series expression. The syntax is as follows:
var1 <per1 per2> = value | list of values | expression; |
The variable name, var1, is here the name of the timeseries that is created or assigned with values. You may prepend SERIES to the statement to indicate the variable type, but this is not necessary (see more on this: +toggle)
In Gekko versions prior to 3.0, you had to start the statement with the SERIES keyword, for instance
This is not necessary in Gekko versions >= 3.0, where you can just write
Note the placement of the local <> option field between the left-hand side variable and the = symbol. The SERIES keyword is not necessary, because Gekko knows from the variable name y that it must be a timeseries (names of other types of variables like scalars and collections must start with % or # symbol).
|
If only one value is specified, this value will be applied to the entire update period. If several values are specified, the number of values must match the number of periods. The following examples set values for the timeseries var1 over the period per1 to per2:
var1 <per1 per2> = value; |
The var1-var4 examples below shows the use of a value, a list of values, or an expression:
time 2021 2023; |
Note that var4 is not simple numbers like var2, and therefore the right-hand side has to defined with parentheses (list syntax). It is possible to define the variable over a specific period of time. For example, if you want to form a Laspeyres chain price index, with 2010 equal to 1, on the basis of two quantities in current prices, q1 and q2, as well as their prices p1 and p2:
reset; |
Result:
p % q % |
First of all, note that in general, you can state a timeseries lag more compactly as x.1 instead of x[-1]. In the formula, (p1*q1 + p2*q2)/(p1.1*q1 + p2.1*q2) means taking the total expenses in the numerator, and the total expenses in the previous year's prices in the denominator. This fraction is used to construct the price index p successively year for year. Note also the <dyn> tag in the formula. Without it, Gekko will complain that because of the accumulating lag p.1, you need to decorate the statement with either <dyn> or <dyn = no> (the latter would not make sense here). Read more about <dyn> and the reason why Gekko does not just run with implicit <dyn> tags added in all cases like this: +toggle.
As mentioned, Gekko will detect if you omit a <dyn> tag in a statement like x = x[-1] + 1;, where the dependent variable x appears with a lag on the right-hand side. Such a statement must be decorated with <dyn> or <dyn = no>, otherwise Gekko fails with an error. What <dyn> does is in reality quite simple. In a timeseries statement like x <dyn> = x[-1] + 1;, instead of running the statement in one go over the whole period (let us assume that the period is 2020-2023), it will run the statement period-by-period in four steps: in 2020, in 2021, in 2022, and in 2023. In that way, lags will accumulate, and x will augment with 1 for each period. If the statement is not run in four steps, it does not accumulate, because timeseries are treated like vectors in Gekko, cf. this explanation page with illustrations etc.
But why doesn't Gekko run such statements stepwise period-by-period on itself? Among other things, the reason is that some kinds of statements cannot be run interchangeably with <dyn> or <dyn = no>, like for instance the following: x = x[-1] + (1, 2, 3, 4);. To a human, it may seem obvious that this means augmenting x with 1 in 2020, 2 in 2021, 3 in 2022, and 4 in 2023. But the statement cannot just be run for one period at a time: there is only 1 period and 4 values, and Gekko will abort with an error because the number of observations and the number of values do not match. Work-arounds for such cases could perhaps be invented, but simplicity is preferred. The simplicity lies in the fact that with <dyn = no> tag, a series statement like x = x[-1] + 1; is run vector-like in one go (without accumulation), whereas with <dyn> tag, the statement is run period by period, accumulating.
A radical solution to this problem would be to decide that all series statements should be run period-by-period (that is, with an implicit <dyn> tag). This would create other problems, however. Perhaps the worst of these problems is that it would make all series statements run much slower, because of the period-by-period looping. Normally, timeseries calculations in Gekko operate like vector arithmetics, for instance adding or multiplying vectors (arrays) on a low machine level, whereas period-by-period looping operates at a much higher and slower machine level. Therefore, unnecessarily decorating a series statement with <dyn> as in y <dyn> = 2 * x; basically makes the statement run n times slower, where n is the number of time periods. The statement still yields the same results as without <dyn>, but for longer time periods and many series statements, the slowdown would be really bad.
|
Instead of constructing the Laspeyres chain index by hand, Gekko has an in-built function to compute these values. The function laspchain() returns a map, containing the two timeseries (aggregated price and quantity).
The following is an example of changing the base year of a variable:
reset; |
The growth rates of p1 and p1a are identical, but p1a has value 1 in 2012 rather than in 2010.
In the following example, the series x1, x2, x3, and x4 are added together into a sum series called xsum.
reset; |
Instead of this, you can define a list of series names and use the sum() function to sum over this list:
//...continued |
Note the use of {}-parentheses. First, it should be noted that we are using a so-called 'naked' list definition, being short hand for the more strict #xlist = ('x1', 'x2', 'x3', 'x4');, more about this in the user guide section on lists. So the elements of #xlist are the four strings 'x1', 'x2', 'x3', 'x4', and not a list of four timeseries objects. Using sum(#xlist) would amount to sum('x1', 'x2', 'x3', 'x4'), and in that case Gekko will complain that it cannot extract a value from a string type. In a sense, the {}-parentheses 'eat' quotes ('), transforming the sum into sum(x1, x2, x3, x4).
If confused about the use of {}-parentheses, try to read the elevator pitch.
The user may aks why Gekko does not just 'know' that sum('x1', 'x2', 'x3', 'x4') is to be understood as the sum of the variables that these string names refer to? But such contextual interpretation is bound to create other problems. For instance, when printing out #xlist, how should Gekko 'know' whether the user wants to have the four raw strings printed, or the four timeseries that the strings refer to? The PRT statement here has the exact same {}-logic:
//...continued |
It is often convenient to use name-parts ('codes') instead of full names, and use lists to compose the names from these name parts. For instance, you may use a name-composition with the name x{#xcodes} in the following way: #xcodes = ('1', '2', '3', '4'); prt x{#xcodes};. This also prints x1, x2, x3, and x4. Note that #xcodes does not use the syntax #xcodes = 1, 2, 3, 4;, because in that case, the four elements become values, and Gekko will complain that it cannot convert values directly into strings (but you could use #xcodes = #xcodes.strings() to convert the list elements into strings).
In addition to sum(), there is a number of other functions that can replace the corresponding expressions on the right side of the equals sign.
Transformations and operators
Gekko also allows you to use a number of operators for easy transformations. Some of the most used are the following:
^= absolute time-change:
x ^= 15; //absolute time-change, same as x <d>= 15; |
%= relative time-change:
x %= 15; //relative time-change, same as x <p>= 15; |
+= absolute change (same period)
x += 15; //absolute change (same period), same as x <m>= 15; |
*= relative change (same period)
x *= 1.15; //absolute change (same period), same as x <q>= 15; |
#= change in growth rate
x #= 15; //absolute change (same period), same as x <mp>= 15; |
Instead of ^=, %=, +=, *= and #=, you may use the equivalent <d>, <p>, <m>, <q>, or <mp> operators, for instance x <d>= 15; instead of x ^= 15; These <> operators correspond exactly with the corresponding operators used in PRT, PLOT, SHEET, etc. There is also a third possibility, namely using left-hand side functions, for instance dif(x) = 15; which is equal to both x ^= 15; and x <d>= 15;
The choice of operator type is mostly a question of taste, and perhaps habit.
If <keep=p> is used as option, Gekko will keep the growth rate of the left-hand series intact after the period over which the series is updated. For instance, x <2020 2025 m keep=p> x = 100; will add 100 to x over the period 2020-25, since <m> is the operator for absolute change (same period). The keep=p setting makes sure that the growth rate of x regarding 2026 and on is the same as before the update.
Data tracing
Gekko timeseries variables support data tracing, that is, the ability for a given timeseries to "remember" how it has been constructed. Consider the statements x1 = 1; x2 = 2; x = x1 + x2;. Data tracing implies that x remembers that it has been produced according to the expression x1 + x2, and x will also remember how both x1 and x2 were constructed (as sub-traces: even if x1 and x2 are subsequently deleted). Data tracing also works through Gekko databanks (.gbk files). Think "Trace Precedents" from Excel, just more powerful. See more here.