Forecasting
A Perfect Storm of Uncertainty
Late in December 2022, much of the U.S. and Canada experienced a bomb cyclone, of which the most striking feature is a precipitous drop in temperature. Some areas saw temperature declines of 20 to 25 degrees Fahrenheit within a 24-hour period. This coincided with Christmas Eve, Christmas, and Boxing Day (in Canada).
I have identified a number of factors contributing to the difficulty in forecasting the loads on these days.
Short-term (i.e., day-ahead) forecast models typically perform well once a new weather paradigm has been established (i.e., once the temperature stays warm/cold in the summer/winter). However, there is often difficulty capturing rapid temperature fluctuations. My favorite metaphor for this phenomenon is the “late-to-the-party” problem. Models often contain lagged-weather variables. That is, the temperature from yesterday (and potentially the day prior) impact the load forecast for today. In the case when the temperature is fairly stable, this is a feature, not a bug. However, when the temperature changes rapidly, the lagged variables can tie the load forecast too much to the prior weather conditions, pulling at the reins. In most cases, this behavior is highly beneficial and adds to the explanatory power of the model. When temperatures drop by 25 degrees within a day, however, trouble arises.
In many regions, the temperatures during this period were the lowest they have been in a very long time. For the sake of hyperbole (of which I am undoubtedly the world’s greatest proponent), let’s just say they were the “lowest temperatures ever!” The following figure illustrates hourly temperatures at one particular weather station from 2016 to current. The most salient feature of the figure is that the lowest temperature during the past 7 years occurred at the very end of the series, on Dec. 23, 2022.
The next figure zooms in on the weeks leading up to and including the temperature drop.
The following figure illustrates the load-temperature relationship for this same area. Dec. 23, 24, and 25 of 2022 are at the extreme cold side of the curve. As a general matter, statistical models tend to perform well within the historical range of the data. Prior to Dec. 23, the range of temperatures extended from about 12 degrees to about 86 degrees. Clearly, Dec. 23 and 24 are outside the range of the data. Prior to those days occurring, it was unclear what would happen. Would the load continue to increase as temperatures declined or would they reach saturation (the point at which all the heating equipment was running continuously) and load would flatten out? As it turns out, the load continued to increase.
The next factor contributing to the trouble was the calendar itself. Christmas occurred on a Sunday, with Christmas Eve on a Saturday, and Boxing Day (which is not an official U.S. holiday) on a Monday. Christmas occurs on a Sunday 1/7th of the time. The short-term models are typically estimated with 3-5 years of historical data, which means the models may never have seen the impact of Christmas on a Sunday. Further exacerbating this is the question of when the holiday would be observed from a business perspective. Some companies were closed on Friday, Dec. 23; others were closed on Monday, Dec. 26; while others (including Itron) were closed on both Friday and Monday.
A few years ago, I wrote a blog discussing an approach for addressing the impact of holidays that are not stationary with regard to the day of week.
If this is not sufficiently complicated, let’s throw post-COVID remote working into the mix. 2016 was the last time Christmas occurred on a Sunday. So, even if the model included data from 2016, the holiday effect on Christmas and the surrounding days’ loads would likely be different from the current circumstances (with fewer people going into offices), thereby providing little by way of useful information.
To wrap this all up into a nice package, we have:
- Rapidly falling temperatures
- The coldest temperatures “ever”
- Holiday on a weekend
- COVID impacts
One good thing that came from all of this was that the temperature forecasts were predicting the dramatic declines days in advance. That is to say, this did not sneak up on anybody. As the holiday weekend approached, there was plenty of advance warning that trouble was on the horizon and human intervention might be warranted.
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>