s <- suppressPackageStartupMessages
s(library(zoo))
s(library(data.table))
s(library(lubridate))
s(library(knitr))
s(library(ggplot2))This notebook uses the outdoor temperature, indoor temperature and indoor airspeed to calculate:
# Parameters
n_hours_in_every_step <- 366 #Rolling mean will be based of the previous 14 days measurementsThe first step to the code is to construct the outdoor rolling mean. In order to do that, first the outdoor hourly measurement is averaged in hours.
# Load and rename columns for consistency -- no space and no upper case column names
outdoor <- fread("~/Github/erdl/thermal_comfort/adaptive_method/data/houses_outdoor_temperature.csv")
colnames(outdoor) <- c("timestamp","outdoor_temperature_f")
# Identify the right types for each column
outdoor$timestamp <- ymd_hms(outdoor$timestamp,tz="HST")
outdoor$outdoor_temperature_f <- as.numeric(outdoor$outdoor_temperature_f)
outdoor$ymd_h <- outdoor$timestamp
#Ensure timestamps are ordered. Database does not guarantee that.
outdoor <- outdoor[order(timestamp)]
# Reset the minute and second to 0, so we can group by ymd_h.
minute(outdoor$ymd_h) <- 0
second(outdoor$ymd_h) <- 0
outdoor <- outdoor[,.(outdoor_temperature=mean(outdoor_temperature_f)),by="ymd_h"]
kable(head(outdoor))| ymd_h | outdoor_temperature |
|---|---|
| 2017-06-09 14:00:00 | 74.96525 |
| 2017-06-09 15:00:00 | 74.63462 |
| 2017-06-09 16:00:00 | 74.31538 |
| 2017-06-09 17:00:00 | 74.83525 |
| 2017-06-09 18:00:00 | 74.88375 |
| 2017-06-09 19:00:00 | 74.44012 |
Next, the hourly temperature is used to calculate the 14 days temperature backwards.
# Invert table so slide window goes backwards
inverted_outdoor <- outdoor[order(-ymd_h)]
# Calculate rolling mean and reverts back the output from the rolling mean.
rolling_mean_outdoor_temperature <- rev(rollapply(inverted_outdoor$outdoor_temperature,
width=n_hours_in_every_step,
FUN=mean))
#
outdoor <- outdoor[n_hours_in_every_step:.N]
outdoor$rolling_mean_outdoor_temperature <- rolling_mean_outdoor_temperature
colnames(outdoor) <- c("timestamp","outdoor_temperature","rolling_mean_outdoor_temperature")To calculate the boundaries and acceptance, besides the outdoor rolling average, we also need the indoor temperature, and air speed. Because the tables are different, i.e. indoor’s temperature and airspeed are hourly, wherewas the outdoor was originally at a minute sampling rate, there is a chance after the rolling average is calculated the number of rows will not match between both tables. This script matches the minimum number of rows between them both.
# load indoor data
indoor <- fread("~/Github/erdl/thermal_comfort/adaptive_method/data/house2_livingroom_temperature.csv")[,.(reading_timestamp,value)]
colnames(indoor) <- c("timestamp","indoor_temperature")
indoor$timestamp <- ymd_hms(indoor$timestamp,tz="HST")## Date in ISO8601 format; converting timezone from UTC to "HST".
# Identify the right types for each column
indoor$timestamp <- ymd_hms(indoor$timestamp,tz="HST")
indoor$outdoor_temperature_f <- as.numeric(outdoor$indoor_temperature)
indoor$ymd_h <- indoor$timestamp
#Ensure timestamps are ordered. Database does not guarantee that.
indoor <- indoor[order(timestamp)]
# Reset the minute and second to 0, so we can group by ymd_h.
minute(indoor$ymd_h) <- 0
second(indoor$ymd_h) <- 0
indoor <- indoor[,.(indoor_temperature=mean(indoor_temperature)),by="ymd_h"]
colnames(indoor) <- c("timestamp","indoor_temperature")dt <- merge(outdoor,indoor,by="timestamp")
# Add Rolling Mean Vector to Simulation Data Column-Wise. Notice this is NOT an inner join.
#min_rows <- min(length(rolling_temperature_f_mean),nrow(simulation_data))
#dt <- simulation_data[1:min_rows]
#dt$rolling_temperature_f_mean <- rolling_temperature_f_mean[1:min_rows]The comfort level boundaries are calculated using the outdoor rolling average adjusted by the indoor airspeed.
calculate_air_speed_adjustment <- function(in_air_speed){
return(0.0153*in_air_speed+0.4333)
}
dt$upper_bound <- 0.31*dt$rolling_mean_outdoor_temperature + 60.5
dt$upper_bound_0_fpm <- dt$upper_bound + calculate_air_speed_adjustment(0)
dt$upper_bound_120_fpm <- dt$upper_bound + calculate_air_speed_adjustment(120)
dt$upper_bound_200_fpm <- dt$upper_bound + calculate_air_speed_adjustment(200)
dt$lower_bound <- 0.31*dt$rolling_mean_outdoor_temperature + 47.9 With the comfort upper and lower bound, we then compare the indoor temperature against them.
dt$is_acceptable_80 <- NA_character_
dt[indoor_temperature > upper_bound]$is_acceptable_80 <- "Unacceptable Hot"
dt[indoor_temperature < lower_bound]$is_acceptable_80 <- "Unacceptable Cold"
dt[indoor_temperature > lower_bound & indoor_temperature < upper_bound]$is_acceptable_80 <- "Acceptable"In addition, how far higher or lower than the upper and lower bound respectively can also be calculated.
dt$degrees_off <- NA_real_
dt[indoor_temperature > upper_bound]$degrees_off <- dt[indoor_temperature > upper_bound]$indoor_temperature - dt[indoor_temperature > upper_bound]$upper_bound
dt[indoor_temperature < lower_bound]$degrees_off <- dt[indoor_temperature < lower_bound]$indoor_temperature - dt[indoor_temperature < lower_bound]$lower_bound
dt[indoor_temperature > lower_bound & indoor_temperature < upper_bound]$degrees_off <- 0Finally, we the output table containing all the calculated information.
kable(head(dt))| timestamp | outdoor_temperature | rolling_mean_outdoor_temperature | indoor_temperature | upper_bound | upper_bound_0_fpm | upper_bound_120_fpm | upper_bound_200_fpm | lower_bound | is_acceptable_80 | degrees_off |
|---|---|---|---|---|---|---|---|---|---|---|
| 2017-10-06 12:00:00 | 77.18779 | 82.29032 | 84.73325 | 86.01000 | 86.44330 | 88.27930 | 89.50330 | 73.41000 | Acceptable | 0.0000000 |
| 2017-10-06 13:00:00 | 76.74112 | 82.27104 | 85.97350 | 86.00402 | 86.43732 | 88.27332 | 89.49732 | 73.40402 | Acceptable | 0.0000000 |
| 2017-10-06 14:00:00 | 76.38004 | 82.25701 | 87.15250 | 85.99967 | 86.43297 | 88.26897 | 89.49297 | 73.39967 | Unacceptable Hot | 1.1528280 |
| 2017-10-06 15:00:00 | 76.25629 | 82.24737 | 87.30000 | 85.99669 | 86.42999 | 88.26599 | 89.48999 | 73.39669 | Unacceptable Hot | 1.3033147 |
| 2017-10-06 16:00:00 | 76.15875 | 82.23663 | 87.22050 | 85.99336 | 86.42666 | 88.26266 | 89.48666 | 73.39336 | Unacceptable Hot | 1.2271438 |
| 2017-10-06 17:00:00 | 75.58500 | 82.22458 | 86.82250 | 85.98962 | 86.42292 | 88.25892 | 89.48292 | 73.38962 | Unacceptable Hot | 0.8328792 |
n_acceptable <- nrow(dt[is_acceptable_80 == "Acceptable"])
n_unacceptable <- nrow(dt[is_acceptable_80 != "Acceptable"])
st <- min(dt$timestamp)
et <- max(dt$timestamp)
p_start_time <- paste0(year(st),"-",month(st),"-",day(st))
p_end_time <- paste0(year(et),"-",month(et),"-",day(et))
p <- ggplot(data = dt) +
geom_point(aes(rolling_mean_outdoor_temperature,indoor_temperature,color=is_acceptable_80)) +
geom_line(aes(rolling_mean_outdoor_temperature,upper_bound_200_fpm),color="black") +
geom_line(aes(rolling_mean_outdoor_temperature,upper_bound_120_fpm),color="black") +
geom_line(aes(rolling_mean_outdoor_temperature,upper_bound_0_fpm),color="black") +
geom_line(aes(rolling_mean_outdoor_temperature,upper_bound),color="black") +
geom_line(aes(rolling_mean_outdoor_temperature,lower_bound),color="black") +
geom_text(aes(max(dt$rolling_mean_outdoor_temperature)+0.2,max(dt$upper_bound_200_fpm), label = "fpm 200", vjust = -1), size = 3) +
geom_text(aes(max(dt$rolling_mean_outdoor_temperature)+0.2,max(dt$upper_bound_120_fpm), label = "fpm 120", vjust = -1), size = 3) +
geom_text(aes(max(dt$rolling_mean_outdoor_temperature)+0.2,max(dt$upper_bound_0_fpm), label = "fpm 0", vjust = -1), size = 3) +
geom_text(aes(max(dt$rolling_mean_outdoor_temperature)+0.2,max(dt$upper_bound)-0.6, label = "no adj", vjust = -1), size = 3) +
geom_text(aes(max(dt$rolling_mean_outdoor_temperature)+0.2,max(dt$lower_bound), label = "no adj", vjust = -1), size = 3) +
theme_minimal() +
scale_colour_manual(values=c("Unacceptable Cold"="#0072B2","Acceptable"="#009E73","Unacceptable Hot"="#D55E00")) +
ggtitle("Adaptive Method - House 2 Living Room",
subtitle = paste0(p_start_time," to ",p_end_time," | Acceptable: ",n_acceptable," ; Unacceptable: ",n_unacceptable)) +
ylab("Indoor Temperature (F)") +
xlab("Rolling Mean Outdoor Temperature (F)")
p