Forecasting
When Does Humidity Make the Meter Spin?
For anyone that has tried to improve a model by changing the specification, you know it is very much a trial and error process. You create variables (vars), add some vars, replace some vars, and hopefully after it’s all said and done, you end up with a model specification that performs better than its predecessor. Recently, I was working to accomplish this very thing. I was updating the specification of some hourly load forecast models in hopes of improving the in-sample and out-of-sample fit and I came across an interesting finding.
I started with a fairly simple hourly model specification using a neural network. Specifically, the model for each hour had the following functional form where X^t is a linear node containing binary variables and trends and some other calendar vars, and H_1^t and H_2^t are sigmoid nodes containing weather data relevant to each hour:
Initially, my weather term was simply dry-bulb temperature. Then I remembered a brown bag seminar we did last year on modeling humidity effects and considered incorporating humidity into the models.
I decided to swap out hourly dry-bulb temperature for an hourly Temperature Humidity Index (THI) to see how that would impact the fit. I first did a little work to optimize the weight on humidity (which turned out to be another trial and error process). This helped a bit, and using the THI variable improved the in-sample and out-of-sample fit for a fairly wide range of weights.
But what’s interesting is that the improved fit was more pronounced during the afternoon and evening hours. In contrast, the improvement in the late night and early morning hours was negligible. This finding led me to ask myself, “does this make sense?”
Discussions with my compadres led to the conclusion that humidity is a complicated beast. Generally, there is an amount of water vapor in the air, but the amount of water that the air can hold depends on the air temperature. The warmer the air, the more water it can hold. As an example, 80-degree air at 50% humidity has about as much water in it as 60-degree air at 100% humidity. If you are not confused yet, read on.
During the day, the temperature rises and the relative humidity falls. As night comes on, the temperature falls and the relative humidity rises. The amount of water in the air does not change much through this cycle (you can see this from the dewpoint temperature, which usually does not vary much during a day). The point is that during the day, knowing relative humidity brings in some extra information because 80 degrees at 50% humidity feels OK, but 80 degrees with 80% humidity feels sticky. In contrast, at night, 60 degrees at 100% humidity feels fine. It will make the front lawn wet, since temperature has reached the dewpoint, but it is not going to make the air conditioner run. Where I live, the relative humidity is close to 100% on many nights of the year.
So the conclusion seems to be that knowing relative humidity or a related measure like dewpoint temperature is very useful in the daytime hours. But at night, there is less variation and therefore less independent information, and as a result, knowing relative humidity is less useful.
I suppose one could argue a number of other reasons why this was the outcome, but I thought it was interesting and worth sharing. Let me know if you have a different take on these results.
Happy holidays everyone!
I started with a fairly simple hourly model specification using a neural network. Specifically, the model for each hour had the following functional form where X^t is a linear node containing binary variables and trends and some other calendar vars, and H_1^t and H_2^t are sigmoid nodes containing weather data relevant to each hour:
Initially, my weather term was simply dry-bulb temperature. Then I remembered a brown bag seminar we did last year on modeling humidity effects and considered incorporating humidity into the models.
I decided to swap out hourly dry-bulb temperature for an hourly Temperature Humidity Index (THI) to see how that would impact the fit. I first did a little work to optimize the weight on humidity (which turned out to be another trial and error process). This helped a bit, and using the THI variable improved the in-sample and out-of-sample fit for a fairly wide range of weights.
But what’s interesting is that the improved fit was more pronounced during the afternoon and evening hours. In contrast, the improvement in the late night and early morning hours was negligible. This finding led me to ask myself, “does this make sense?”
Discussions with my compadres led to the conclusion that humidity is a complicated beast. Generally, there is an amount of water vapor in the air, but the amount of water that the air can hold depends on the air temperature. The warmer the air, the more water it can hold. As an example, 80-degree air at 50% humidity has about as much water in it as 60-degree air at 100% humidity. If you are not confused yet, read on.
During the day, the temperature rises and the relative humidity falls. As night comes on, the temperature falls and the relative humidity rises. The amount of water in the air does not change much through this cycle (you can see this from the dewpoint temperature, which usually does not vary much during a day). The point is that during the day, knowing relative humidity brings in some extra information because 80 degrees at 50% humidity feels OK, but 80 degrees with 80% humidity feels sticky. In contrast, at night, 60 degrees at 100% humidity feels fine. It will make the front lawn wet, since temperature has reached the dewpoint, but it is not going to make the air conditioner run. Where I live, the relative humidity is close to 100% on many nights of the year.
So the conclusion seems to be that knowing relative humidity or a related measure like dewpoint temperature is very useful in the daytime hours. But at night, there is less variation and therefore less independent information, and as a result, knowing relative humidity is less useful.
I suppose one could argue a number of other reasons why this was the outcome, but I thought it was interesting and worth sharing. Let me know if you have a different take on these results.
Happy holidays everyone!
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>