Industry Insights
Dividing Two Interval Series in MetrixLT
In my previous post, I showed that MetrixLT can multiply two hourly data series even though the software was not designed for that specific purpose.
Finding unintended uses of MetrixLT proved to be an addicting game for our forecasting staff.
Naturally, the next question is: “If we can multiply, can we divide?”
Since division is multiplication with an inverse, we thought this might be easy. But once again, MetrixLT was not designed for performing simple arithmetic, and creating the inverse still requires dividing.
The MetrixLT Scaling Transformation is designed for two functions. First, The Scaling Transform can be used to adjust a forecast based on the historic ratio between the backcast and a Target series. If I have a forecast of hourly load for 2018 and a backcast covering 2017 (defined as the Overlap), then I can adjust the forecast based on the ratio of the Target series to the Overlap. While interesting, this is not the designed operation we will use for division.
To divide, we will use the second Scaling Transformation function designed to calibrate a bottom-up forecast to a Target series. Assume you have a Target series Y and three bottom-up series A, B, and C. The Scaling Transform can adjust A, B, and C such that Y= A+B+C. The process is two-fold. First, the Scaling Transform calculates the Calibration series (kCalib) as shown below.
Import the hourly data in the Interval Data table. In this example, hourly data for Zone 1 and Zone 2 are imported. I’ve highlighted the January 11, 2015 values to check our work.
Create a Scaling Transformation and configure the Forecast Variable and the Input Series boxes. In the Forecast Variable box, insert the hourly data used as the numerator. In the Input Series box insert the hourly data used as the denominator. In the example, Zone1 is the numerator and Zone 2 is the denominator.
When the Target Variable is undefined, the Energy Method, Peak Method and Calibration Method selections do not apply.
I’ve highlighted the validation for January 11, 2015.
Zone 1 values are divided by Zone 2 values and stored in a kCalib variable of the Scaling Transformation table.
Finding unintended uses of MetrixLT proved to be an addicting game for our forecasting staff.
Naturally, the next question is: “If we can multiply, can we divide?”
Since division is multiplication with an inverse, we thought this might be easy. But once again, MetrixLT was not designed for performing simple arithmetic, and creating the inverse still requires dividing.
The MetrixLT Scaling Transformation is designed for two functions. First, The Scaling Transform can be used to adjust a forecast based on the historic ratio between the backcast and a Target series. If I have a forecast of hourly load for 2018 and a backcast covering 2017 (defined as the Overlap), then I can adjust the forecast based on the ratio of the Target series to the Overlap. While interesting, this is not the designed operation we will use for division.
To divide, we will use the second Scaling Transformation function designed to calibrate a bottom-up forecast to a Target series. Assume you have a Target series Y and three bottom-up series A, B, and C. The Scaling Transform can adjust A, B, and C such that Y= A+B+C. The process is two-fold. First, the Scaling Transform calculates the Calibration series (kCalib) as shown below.
kCalib = Y/(A+B+C).
Second, the Scaling Transform applies the Calibration series to each of the bottom-up series.A’ = A x kCalib
B’ = B x kCalib
C’ = C x kCalib
The division process takes place in calculating kCalib. To divide interval data, we must make Y one series and the bottom up components (A, B, and C) and single a different series (D). The resulting ratio is the division outcome.kCalib = Y/D.
Division can be accomplished using the following steps.Step 1: Import Interval Data
Import the hourly data in the Interval Data table. In this example, hourly data for Zone 1 and Zone 2 are imported. I’ve highlighted the January 11, 2015 values to check our work.
Step 2: Configure A Scaling Transformation
Create a Scaling Transformation and configure the Forecast Variable and the Input Series boxes. In the Forecast Variable box, insert the hourly data used as the numerator. In the Input Series box insert the hourly data used as the denominator. In the example, Zone1 is the numerator and Zone 2 is the denominator.
When the Target Variable is undefined, the Energy Method, Peak Method and Calibration Method selections do not apply.
Step 3: Calculate the Result
Select the “!” to calculate the Scaling Transformation. The division result is stored in the kCalib variable.I’ve highlighted the validation for January 11, 2015.
- Zone 1 = 2.60
- Zone 2 = 36.595
- Product = (Zone 1) / (Zone 2) = 0.071048
Zone 1 values are divided by Zone 2 values and stored in a kCalib variable of the Scaling Transformation table.
Si è verificato un errore nell'elaborarazione del modello.
The following has evaluated to null or missing:
==> authorContent.contentFields [in template "44616#44647#114455" at line 9, column 17]
----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: contentFields = authorContent.content... [in template "44616#44647#114455" at line 9, column 1]
----
1<#assign
2 webContentData = jsonFactoryUtil.createJSONObject(author.getData())
3 classPK = webContentData.classPK
4/>
5
6<#assign
7authorContent = restClient.get("/headless-delivery/v1.0/structured-contents/" + classPK + "?fields=contentFields%2CfriendlyUrlPath%2CtaxonomyCategoryBriefs")
8contentFields = authorContent.contentFields
9categories=authorContent.taxonomyCategoryBriefs
10authorContentData = jsonFactoryUtil.createJSONObject(authorContent)
11friendlyURL = authorContentData.friendlyUrlPath
12authorCategoryId = "0"
13/>
14
15<#list contentFields as contentField >
16 <#assign
17 contentFieldData = jsonFactoryUtil.createJSONObject(contentField)
18 name = contentField.name
19 />
20 <#if name == 'authorImage'>
21 <#if (contentField.contentFieldValue.image)??>
22 <#assign authorImageURL = contentField.contentFieldValue.image.contentUrl />
23 </#if>
24 </#if>
25 <#if name == 'authorName'>
26 <#assign authorName = contentField.contentFieldValue.data />
27 <#list categories as category >
28 <#if authorName == category.taxonomyCategoryName>
29 <#assign authorCategoryId = category.taxonomyCategoryId />
30 </#if>
31 </#list>
32 </#if>
33 <#if name == 'authorDescription'>
34 <#assign authorDescription = contentField.contentFieldValue.data />
35
36 </#if>
37
38 <#if name == 'authorJobTitle'>
39 <#assign authorJobTitle = contentField.contentFieldValue.data />
40
41 </#if>
42
43</#list>
44
45<div class="blog-author-info">
46 <#if authorImageURL??>
47 <img class="blog-author-img" id="author-image" src="${authorImageURL}" alt="" />
48 </#if>
49 <#if authorName??>
50 <#if authorName != "">
51 <p class="blog-author-name">By <a id="author-detail-page" href="/w/${friendlyURL}?filter_category_552298=${authorCategoryId}"><span id="author-full-name">${authorName}</span></a></p>
52 <hr />
53 </#if>
54 </#if>
55 <#if authorJobTitle??>
56 <#if authorJobTitle != "">
57 <p class="blog-author-title" id="author-job-title" >${authorJobTitle}</p>
58 <hr />
59 </#if>
60 </#if>
61 <#if authorDescription??>
62 <#if authorDescription != "" && authorDescription != "null" >
63 <p class="blog-author-desc" id="author-job-desc">${authorDescription}</p>
64 <hr />
65 </#if>
66 </#if>
67</div>
The following has evaluated to null or missing: ==> authorContent.contentFields [in template "44616#44647#114455" at line 9, column 17] ---- Tip: It's the step after the last dot that caused this error, not those before it. ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: contentFields = authorContent.content... [in template "44616#44647#114455" at line 9, column 1] ----
1<#assign
2 webContentData = jsonFactoryUtil.createJSONObject(author.getData())
3 classPK = webContentData.classPK
4/>
5
6<#assign
7authorContent = restClient.get("/headless-delivery/v1.0/structured-contents/" + classPK + "?fields=contentFields%2CfriendlyUrlPath%2CtaxonomyCategoryBriefs")
8contentFields = authorContent.contentFields
9categories=authorContent.taxonomyCategoryBriefs
10authorContentData = jsonFactoryUtil.createJSONObject(authorContent)
11friendlyURL = authorContentData.friendlyUrlPath
12authorCategoryId = "0"
13/>
14
15<#list contentFields as contentField >
16 <#assign
17 contentFieldData = jsonFactoryUtil.createJSONObject(contentField)
18 name = contentField.name
19 />
20 <#if name == 'authorImage'>
21 <#if (contentField.contentFieldValue.image)??>
22 <#assign authorImageURL = contentField.contentFieldValue.image.contentUrl />
23 </#if>
24 </#if>
25 <#if name == 'authorName'>
26 <#assign authorName = contentField.contentFieldValue.data />
27 <#list categories as category >
28 <#if authorName == category.taxonomyCategoryName>
29 <#assign authorCategoryId = category.taxonomyCategoryId />
30 </#if>
31 </#list>
32 </#if>
33 <#if name == 'authorDescription'>
34 <#assign authorDescription = contentField.contentFieldValue.data />
35
36 </#if>
37
38 <#if name == 'authorJobTitle'>
39 <#assign authorJobTitle = contentField.contentFieldValue.data />
40
41 </#if>
42
43</#list>
44
45<div class="blog-author-info">
46 <#if authorImageURL??>
47 <img class="blog-author-img" id="author-image" src="${authorImageURL}" alt="" />
48 </#if>
49 <#if authorName??>
50 <#if authorName != "">
51 <p class="blog-author-name">By <a id="author-detail-page" href="/w/${friendlyURL}?filter_category_552298=${authorCategoryId}"><span id="author-full-name">${authorName}</span></a></p>
52 <hr />
53 </#if>
54 </#if>
55 <#if authorJobTitle??>
56 <#if authorJobTitle != "">
57 <p class="blog-author-title" id="author-job-title" >${authorJobTitle}</p>
58 <hr />
59 </#if>
60 </#if>
61 <#if authorDescription??>
62 <#if authorDescription != "" && authorDescription != "null" >
63 <p class="blog-author-desc" id="author-job-desc">${authorDescription}</p>
64 <hr />
65 </#if>
66 </#if>
67</div>