0.1 Libraries

library(httr)
library(data.table)
library(dplyr)
library(lubridate)
library(knitr)
library(highcharter)
library(DT)
library(caret)
library(tibble) 
library(rsample)   
library(jtools)
library(tidyr)

1 Daten importieren und vorbereiten

1.1 Meteorologische Daten

fread("https://data.geo.admin.ch/ch.meteoschweiz.klima/nbcn-tageswerte/nbcn-daily_BAS_previous.csv", sep = ";", colClasses = c("character", "Date", rep("numeric", 10))) %>%
  mutate(
    timestamp = as.POSIXct(
      paste0(
        substr(date, 1, 4),
        "-",
        substr(date, 5, 6),
        "-",
        substr(date, 7, 8),
        " 00:00:00"),
      format="%Y-%m-%d %H:%M:%S"
    )
  ) %>%
  mutate_at(c("gre000d0", "hto000d0", "nto000d0", "prestad0", "rre150d0", "sre000d0", "tre200d0", "tre200dn", "tre200dx", "ure200d0"), as.numeric) %>%
  bind_rows(
    # fread("test2.csv", sep = ";", colClasses = c("character", "Date", rep("numeric", 10))) %>%
    fread("https://data.geo.admin.ch/ch.meteoschweiz.klima/nbcn-tageswerte/nbcn-daily_BAS_current.csv", sep = ";", colClasses = c("character", "Date", rep("numeric", 10))) %>%
      mutate(
        timestamp = as.POSIXct(
          paste0(
            substr(date, 1, 4),
            "-",
            substr(date, 5, 6),
            "-",
            substr(date, 7, 8),
            " 00:00:00"),
          format="%Y-%m-%d  %H:%M:%S", tz="Europe/Berlin"
        )
      ) %>%
      mutate_at(c("gre000d0", "hto000d0", "nto000d0", "prestad0", "rre150d0", "sre000d0", "tre200d0", "tre200dn", "tre200dx", "ure200d0"), as.numeric)
  ) %>%
  relocate(timestamp) %>%
  select(-c(date, `station/location`)) %>%
  mutate(
    year = as.numeric(substr(timestamp, 1, 4)),
    month = as.numeric(substr(timestamp, 6, 7)),
    day = as.numeric(substr(timestamp, 9, 10))
  ) %>%
  data.frame() %>%
  assign("meteo", ., inherits = TRUE)
meteo %>%
         mutate(Date=as.Date(timestamp)) %>%
           select(Date, max_temp = tre200dx, min_temp = tre200dn, mean_temp = tre200d0) %>%
           filter(year(Date)>1970) -> temp

grafik_meteo <- highchart(type = "stock") %>% 
  hc_add_series(type = "line", 
                data = temp, 
                hcaes(Date, mean_temp), 
                color = "#B00000",
                tooltip = list(pointFormat = "Tagesmittel: {point.mean_temp:.1f} C"),
                size = 0.15) %>%
  hc_add_series(type = "line", 
                data = temp, 
                hcaes(Date, max_temp), 
                color = "#661200",
                tooltip = list(pointFormat = "Tagesmaximum: {point.max_temp:.1f} C"),
                size = 0.15) %>%
  hc_add_series(type = "line", 
                data = temp, 
                hcaes(Date, min_temp), 
                color = "#DC440E",
                tooltip = list(pointFormat = "Tagesminimum: {point.min_temp:.1f} C"),
                size = 0.15) %>%
  hc_rangeSelector(verticalAlign = "bottom",
                   selected = 1) %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = ""), opposite = F)

grafik_meteo

Figure 1.1: Lufttemperatur seit 1864.



1.2 Stromverbrauchs-Daten

httr::GET("https://data.bs.ch/explore/dataset/100233/download/?format=csv&timezone=Europe%2FBerlin")  %>%
  content(., "text") %>%
  fread(sep = ";", colClasses = c(timestamp_interval_start_text = "character")) %>%
  select(timestamp = timestamp_interval_start_text, netzlast_kwh = stromverbrauch_kwh, grundversorgte_kunden_kwh, freie_kunden_kwh) %>%
  arrange(timestamp) %>%
  mutate(
    year = as.numeric(substr(timestamp, 1, 4)),
    month = as.numeric(substr(timestamp, 6, 7)),
    day = as.numeric(substr(timestamp, 9, 10))
  ) %>%
  group_by(year, month, day) %>%
  filter(year > 2011) %>%
  summarise(netzlast_kwh = sum(netzlast_kwh, na.rm = T),
            grundversorgte_kunden_kwh = sum(grundversorgte_kunden_kwh, na.rm = T),
            freie_kunden_kwh = sum(freie_kunden_kwh, na.rm = T)) %>%
  ungroup() %>%
  mutate(timestamp = as.POSIXct(
    paste0(year, "-", month, "-", day, " 00:00:00"), format = "%Y-%m-%d  %H:%M:%S", tz = "Europe/Berlin")
  ) %>%
  assign("strom_daily", ., inherits = TRUE)



grafik_Stromverbrauch <- highchart(type = "stock") %>% 
  hc_add_series(strom_daily %>%
         mutate(Date = as.Date(timestamp),
                netzlast_kwh = netzlast_kwh/1000000), 
         type = "line", 
         hcaes(Date, netzlast_kwh), 
         color = "#923F8D",
         tooltip = list(pointFormat = "Stromverbrauch: {point.netzlast_kwh:.2f} GWh"),
         size = 0.15) %>%
  hc_rangeSelector(verticalAlign = "bottom",
                   selected = 1) %>%
  hc_xAxis(title = list(text = "")) %>% 
  hc_yAxis(title = list(text = ""), opposite = F)

grafik_Stromverbrauch

Figure 1.2: Täglicher Stromverbrauch seit 2012.



1.3 Feiertage, Ferien und Veranstaltungen

httr::GET("https://data.bs.ch/explore/dataset/100074/download/?format=csv&timezone=Europe%2FBerlin") %>%
  content(., "text") %>%
  fread(sep = ";") %>%
  select(tag_datum, name, code, kategorie_name) %>%
  filter(name != "Fasnachtsmontag", name != "Fasnachtsmittwoch", name != "Dies Academicus") %>%
  filter(!(tag_datum == "2008-05-01 00:00:00" & name == "Tag der Arbeit")) %>% # Tag der Arbeit doubles with Auffahrt
  filter(kategorie_name %in% c("Feiertag", "Ferien") | code == "herbstm") %>%
  filter(name != "Semesterferien") %>%
  assign("rd_veranst", ., inherits = TRUE)

rd_veranst %>%
  data.frame() %>%
  mutate(Herbstmesse = if_else(code == "herbstm", "Herbstmesse", "")) %>%
  select(tag_datum, Herbstmesse) %>%
  filter(Herbstmesse != "") %>%
  full_join(
    rd_veranst %>%
      mutate(Feiertage = if_else(kategorie_name == "Feiertag", name, "")) %>%
      select(tag_datum, Feiertage) %>%
      filter(Feiertage != ""),
    by = "tag_datum"
  ) %>%
  full_join(
    rd_veranst %>%
      mutate(Ferien = if_else(kategorie_name == "Ferien", name, "")) %>%
      select(tag_datum, Ferien) %>%
      filter(Ferien != ""),
    by = "tag_datum"
  ) %>%
  mutate(timestamp = as.POSIXct(tag_datum, tz="Europe/Berlin")) %>%
  filter(year(timestamp) > 2011) %>%
  select(timestamp, Herbstmesse, Feiertage, Ferien) %>%
  mutate(
    year = lubridate::year(timestamp),
    month = lubridate::month(lubridate::floor_date(timestamp, "month")),
    day = lubridate::day(lubridate::floor_date(timestamp, "day"))
  ) %>%
  data.frame() %>%
  assign("Veranstaltungen", ., inherits = TRUE)

rm(events, rd_veranst)



1.4 Datensätze zusammenfügen

meteo %>%
  full_join(Veranstaltungen %>%
              select(-timestamp), by = c("year" = "year", "month" = "month", "day" = "day")) %>%
  full_join(strom_daily %>%
              select(-timestamp), by = c("year" = "year", "month" = "month", "day" = "day")) %>%
  # mutate(weekday = lubridate::wday(timestamp, label = TRUE, abbr = TRUE, locale="German_Germany"),
  #        daytype = if_else(weekday %in% c("So", "Sa"), "Wochenende", "Werktage")) %>%
  mutate(weekday = factor(lubridate::wday(timestamp), ordered=FALSE),
         daytype = if_else(weekday %in% c(1,7), "Wochenende", "Werktage")) %>%
  mutate(Covid19_Lockdown = if_else(timestamp < as.POSIXct("2020-03-15", format="%Y-%m-%d"), 0, 
                                    if_else(timestamp > as.POSIXct("2020-05-10", format="%Y-%m-%d"), 0, 1))
  ) %>%
  relocate(timestamp, netzlast_kwh, grundversorgte_kunden_kwh, freie_kunden_kwh) %>%
  filter(year(timestamp) > 2011) %>%
  mutate(HGT = if_else(tre200d0 <= 12, 20-tre200d0, 0)) %>%
  mutate(Herbstmesse = if_else(is.na(Herbstmesse), "No", Herbstmesse),
         Feiertage = if_else(is.na(Feiertage), "No", Feiertage),
         Ferien = if_else(is.na(Ferien), "No", Ferien),
         HGT_sq = HGT^2) %>%
  drop_na() %>%
  mutate(time = as.Date(timestamp, tz = 'Europe/Berlin')) %>%
  select(-timestamp) %>%
  relocate(time) %>%
  assign("Data", ., inherits = TRUE)

Data %>%
  mutate(time = as.Date(time),
         Feiertage_dummy = if_else(Feiertage == "No", 0, 1),
         Ferien_dummy = if_else(Ferien == "No", 0, 1),
         Herbstmesse_dummy = if_else(Herbstmesse == "No", 0, 1),
         Wochenende_dummy = if_else(daytype == "Werktage", 0, 1),
         Mo_dummy = if_else(weekday == 2, 1, 0),
         Di_dummy = if_else(weekday == 3, 1, 0),
         Mi_dummy = if_else(weekday == 4, 1, 0),
         Do_dummy = if_else(weekday == 5, 1, 0),
         Fr_dummy = if_else(weekday == 6, 1, 0),
         Sa_dummy = if_else(weekday == 7, 1, 0),
         So_dummy = if_else(weekday == 1, 1, 0),
         time_num = as.numeric(time),
         Neujahrstag_dummy = if_else(Feiertage == "Neujahrstag", 1, 0),
         Karfreitag_dummy = if_else(Feiertage == "Karfreitag", 1, 0),
         Ostersonntag_dummy = if_else(Feiertage == "Ostersonntag", 1, 0),
         Ostermontag_dummy = if_else(Feiertage == "Ostermontag", 1, 0),
         Tag_der_Arbeit_dummy = if_else(Feiertage == "Tag der Arbeit", 1, 0),
         Auffahrt_dummy = if_else(Feiertage == "Auffahrt", 1, 0),
         Pfingssonntag_dummy = if_else(Feiertage == "Pfingssonntag", 1, 0),
         Pfingstmontag_dummy = if_else(Feiertage == "Pfingstmontag", 1, 0),
         Bundesfeiertag_dummy = if_else(Feiertage == "Bundesfeiertag", 1, 0),
         Heiligabend_dummy = if_else(Feiertage == "Heiligabend", 1, 0),
         Weihnachten_dummy = if_else(Feiertage == "Weihnachten", 1, 0),
         Stephanstag_dummy = if_else(Feiertage == "Stephanstag", 1, 0),
         Silvester_dummy = if_else(Feiertage == "Silvester", 1, 0),
         Weihnachtsferien_dummy = if_else(Feiertage == "Weihnachtsferien", 1, 0),
         Fasnachtsferien_dummy = if_else(Feiertage == "Fasnachtsferien (Sportferien)", 1, 0),
         Osterferien_dummy = if_else(Feiertage == "Osterferien (Frühlingsferien)", 1, 0),
         Sommerferien_dummy = if_else(Feiertage == "Sommerferien", 1, 0),
         Herbstferien_dummy = if_else(Feiertage == "Herbstferien", 1, 0),
         tre200d0_sq = tre200d0^2
  ) %>%
  dplyr::select(-c(grundversorgte_kunden_kwh, freie_kunden_kwh, year, month, day, Herbstmesse, Feiertage, Ferien, daytype, weekday)) %>%
  slice(1:(n() - 1)) %>%
  assign("Data_selec", ., inherits = TRUE)



2 OLS Regression

Data_selec_model <- Data_selec %>%
  filter(time < as.Date("2025-09-30"))

set.seed(12345)

inTraining <- createDataPartition(Data_selec_model$netzlast_kwh, p = .7, list = FALSE)
training <- Data_selec_model[ inTraining,]
testing  <- Data_selec_model[-inTraining,]

fitControl <- trainControl(method = "repeatedcv",
                           number = 10,
                           repeats = 10)


set.seed(54321)
ols_Fit1 <- train(netzlast_kwh ~ time_num + I(time_num^2) + Di_dummy + Mi_dummy + Do_dummy + Fr_dummy + Sa_dummy + So_dummy + gre000d0 + hto000d0 + 
                    nto000d0 + prestad0 +
                    rre150d0 + sre000d0 + tre200d0 + I(tre200d0^2) + 
                    tre200dn + tre200dx +
                    ure200d0 + Covid19_Lockdown + HGT + I(HGT^2) + Neujahrstag_dummy + Karfreitag_dummy + 
                    Ostersonntag_dummy +
                    Ostermontag_dummy + Tag_der_Arbeit_dummy + Auffahrt_dummy + 
                    Pfingssonntag_dummy +
                    Pfingstmontag_dummy + Bundesfeiertag_dummy + Heiligabend_dummy + Weihnachten_dummy + Stephanstag_dummy + Silvester_dummy + Ferien_dummy + 
                    time_num * Wochenende_dummy + 
                    Neujahrstag_dummy * Wochenende_dummy +
                    Tag_der_Arbeit_dummy * Wochenende_dummy +
                    Bundesfeiertag_dummy  * Wochenende_dummy +
                    Heiligabend_dummy * Wochenende_dummy +
                    Stephanstag_dummy * Wochenende_dummy,
                  data = training, 
                  method = "lm",
                  trControl = fitControl,
                  verbose = TRUE)

# ols_Fit1
# ols_Fit1$resample
# summary(ols_Fit1)

2.1 Leistung cross-validation

ols_Fit1$results %>% 
  round(digits = 3) -> ols_Fit1$results


data.frame(
  RMSE = paste0(ols_Fit1$results[2], " &pm; ", ols_Fit1$results[5]),
  Rsquared = paste0(ols_Fit1$results[3], " &pm; ", ols_Fit1$results[6]),
  MAE = paste0(ols_Fit1$results[4], " &pm; ", ols_Fit1$results[7])
) %>%
  kable()
RMSE Rsquared MAE
106863.87 ± 6146.819 0.952 ± 0.006 80160.168 ± 3725.154

2.2 Endgültiges Modell

final_model <- lm(netzlast_kwh ~ time_num + I(time_num^2) + Di_dummy + Mi_dummy + Do_dummy + Fr_dummy + Sa_dummy + So_dummy + gre000d0 + hto000d0 + #nto000d0 + prestad0 +
                    rre150d0 + sre000d0 + tre200d0 + I(tre200d0^2) + #tre200dn + tre200dx +
                    ure200d0 + Covid19_Lockdown + HGT + I(HGT^2) + Neujahrstag_dummy + Karfreitag_dummy + 
                    Ostersonntag_dummy + 
                    Ostermontag_dummy + Tag_der_Arbeit_dummy + Auffahrt_dummy + 
                    Pfingssonntag_dummy +
                    Pfingstmontag_dummy + Bundesfeiertag_dummy + Heiligabend_dummy + Weihnachten_dummy + Stephanstag_dummy + Silvester_dummy + Ferien_dummy + 
                    time_num * Wochenende_dummy + 
                    Neujahrstag_dummy * Wochenende_dummy +
                    Tag_der_Arbeit_dummy * Wochenende_dummy +
                    Bundesfeiertag_dummy  * Wochenende_dummy +
                    Heiligabend_dummy * Wochenende_dummy +
                    Stephanstag_dummy * Wochenende_dummy,
                  data = Data_selec_model)


summ(final_model,
     model.info = T,
     model.fit = F,
     digits = getOption("jtools-digits", 3),
     stars = T,
     robust=T
)
Observations 5021
Dependent variable netzlast_kwh
Type OLS linear regression
Est. S.E. t val. p
(Intercept) 15602058.149 269182.260 57.961 0.000
time_num -1060.632 29.406 -36.069 0.000
I(time_num^2) 0.023 0.001 28.320 0.000
Di_dummy 53893.532 5429.682 9.926 0.000
Mi_dummy 53427.713 5579.311 9.576 0.000
Do_dummy 61864.917 5718.828 10.818 0.000
Fr_dummy 12244.128 5854.332 2.091 0.037
Sa_dummy -1888615.556 42106.422 -44.853 0.000
So_dummy -2166471.382 42208.219 -51.328 0.000
gre000d0 -930.506 45.065 -20.648 0.000
hto000d0 -3977.760 2455.977 -1.620 0.105
rre150d0 1105.038 336.676 3.282 0.001
sre000d0 96.814 12.941 7.481 0.000
tre200d0 -53357.812 4924.641 -10.835 0.000
I(tre200d0^2) 2442.975 131.367 18.597 0.000
ure200d0 1218.458 204.046 5.971 0.000
Covid19_Lockdown -242754.906 18957.585 -12.805 0.000
HGT 17995.444 1099.750 16.363 0.000
I(HGT^2) -1313.945 149.485 -8.790 0.000
Neujahrstag_dummy -834218.088 71834.111 -11.613 0.000
Karfreitag_dummy -676838.278 54358.580 -12.451 0.000
Ostersonntag_dummy -89345.930 44238.847 -2.020 0.043
Ostermontag_dummy -681755.845 56709.337 -12.022 0.000
Tag_der_Arbeit_dummy -744137.264 46553.990 -15.984 0.000
Auffahrt_dummy -743688.858 42711.847 -17.412 0.000
Pfingssonntag_dummy -117795.482 31532.656 -3.736 0.000
Pfingstmontag_dummy -748042.229 51692.555 -14.471 0.000
Bundesfeiertag_dummy -679846.125 49974.250 -13.604 0.000
Heiligabend_dummy -486468.770 36750.916 -13.237 0.000
Weihnachten_dummy -718707.750 98966.435 -7.262 0.000
Stephanstag_dummy -807312.022 49500.922 -16.309 0.000
Silvester_dummy -356511.060 71512.733 -4.985 0.000
Ferien_dummy -121051.201 3965.246 -30.528 0.000
Wochenende_dummy NA NA NA NA
time_num:Wochenende_dummy 78.317 2.325 33.679 0.000
Neujahrstag_dummy:Wochenende_dummy 610558.515 95134.388 6.418 0.000
Tag_der_Arbeit_dummy:Wochenende_dummy 590246.084 144138.048 4.095 0.000
Bundesfeiertag_dummy:Wochenende_dummy 503491.484 112027.816 4.494 0.000
Heiligabend_dummy:Wochenende_dummy 397046.388 49050.455 8.095 0.000
Stephanstag_dummy:Wochenende_dummy 500453.172 135508.688 3.693 0.000
Standard errors: Robust, type = HC3
RMSE Rsquared MAE
103938.2 0.954 78765.71



Data_selec %>%
  bind_cols(
    predict(final_model, Data_selec, interval = "prediction")
  ) %>%
  mutate(netzlast_kwh = netzlast_kwh/1000000,
         fit = fit/1000000,
         lwr = lwr/1000000,
         upr = upr/1000000) -> Data_model_ols



grafik_output <- highchart(type = "stock") %>%
  hc_add_series(Data_model_ols, "line", hcaes(time, netzlast_kwh), color = "#008AC3",
       tooltip = list(pointFormat = "Effektiver Stromverbrauch: {point.netzlast_kwh:.2f} GWh",
                      shared = TRUE),
       zIndex = 1) %>%
  hc_add_series(Data_model_ols, "line", hcaes(time, fit), color = "#B375AB",
       tooltip = list(pointFormat = "Erwarteter Stromverbrauch: {point.fit:.2f} GWh",
                      shared = TRUE),
       zIndex = 2) %>%
  hc_add_series(Data_model_ols, type = "arearange",
                hcaes(x = time, low = lwr, high = upr),
                zIndex = 0,
                color = "#E7CEE2",
                tooltip = list(pointFormat = "95% Konfidenzintervall: {point.lwr:.2f} - {point.upr:.2f} GWh"), shared = TRUE
                ) %>%
  hc_xAxis(title = list(text = ""), opposite = FALSE) %>% 
  hc_yAxis(title = list(text = "")) %>%
  hc_plotOptions(series = list(marker = list(enabled = FALSE))) %>%
  hc_rangeSelector(selected = 0)

grafik_output

Figure 2.1: Effektiver und erwarteter täglicher Stromverbrauch.

write.csv2(Data_model_ols %>%
             mutate(vgl_real_minus_forecast = netzlast_kwh - fit) %>%
             select(
               time,
               stromverbrauch = netzlast_kwh,
               forecast = fit,
               vgl_real_minus_forecast,
               forecast_lowFI = lwr,
               forecast_highFI = upr
               ), 
           "100245.csv", row.names = F, na = "") 




Hinweis: Je nach Kombination von Betriebssystemen und Versionen von RStudio, R und den verwendeten Pakete können die Ergebnisse leicht von den publizierten Resultaten abweichen. Die angewendete Konfiguration lautet:


sessionInfo()
## R version 4.3.3 (2024-02-29 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 11 x64 (build 22631)
## 
## Matrix products: default
## 
## 
## locale:
## [1] LC_COLLATE=German_Switzerland.utf8  LC_CTYPE=German_Switzerland.utf8   
## [3] LC_MONETARY=German_Switzerland.utf8 LC_NUMERIC=C                       
## [5] LC_TIME=German_Switzerland.utf8    
## 
## time zone: Europe/Zurich
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices datasets  utils     methods   base     
## 
## other attached packages:
##  [1] tidyr_1.3.1       jtools_2.3.0      rsample_1.2.1     tibble_3.2.1     
##  [5] caret_6.0-94      lattice_0.22-6    ggplot2_3.5.0     DT_0.33          
##  [9] highcharter_0.9.4 knitr_1.46        lubridate_1.9.3   dplyr_1.1.4      
## [13] data.table_1.15.4 httr_1.4.7       
## 
## loaded via a namespace (and not attached):
##  [1] pROC_1.18.5          sandwich_3.1-1       rlang_1.1.3         
##  [4] magrittr_2.0.3       furrr_0.3.1          compiler_4.3.3      
##  [7] systemfonts_1.0.6    vctrs_0.6.5          reshape2_1.4.4      
## [10] stringr_1.5.1        pkgconfig_2.0.3      fastmap_1.1.1       
## [13] backports_1.4.1      pander_0.6.5         utf8_1.2.4          
## [16] rmarkdown_2.26       prodlim_2023.08.28   purrr_1.0.2         
## [19] xfun_0.49            cachem_1.0.8         jsonlite_1.8.8      
## [22] recipes_1.0.10       broom_1.0.5          parallel_4.3.3      
## [25] R6_2.5.1             bslib_0.7.0          stringi_1.8.3       
## [28] rlist_0.4.6.2        parallelly_1.37.1    rpart_4.1.23        
## [31] jquerylib_0.1.4      Rcpp_1.0.12          bookdown_0.41       
## [34] assertthat_0.2.1     iterators_1.0.14     future.apply_1.11.2 
## [37] zoo_1.8-12           Matrix_1.6-5         splines_4.3.3       
## [40] nnet_7.3-19          igraph_2.0.3         timechange_0.3.0    
## [43] tidyselect_1.2.1     rstudioapi_0.16.0    yaml_2.3.8          
## [46] timeDate_4032.109    codetools_0.2-20     curl_5.2.1          
## [49] listenv_0.9.1        plyr_1.8.9           quantmod_0.4.26     
## [52] withr_3.0.0          evaluate_0.23        future_1.33.2       
## [55] survival_3.7-0       xml2_1.3.6           xts_0.13.2          
## [58] pillar_1.9.0         renv_1.0.11          foreach_1.5.2       
## [61] stats4_4.3.3         generics_0.1.3       TTR_0.24.4          
## [64] munsell_0.5.1        scales_1.3.0         globals_0.16.3      
## [67] class_7.3-22         glue_1.7.0           tools_4.3.3         
## [70] ModelMetrics_1.2.2.2 gower_1.0.1          forcats_1.0.0       
## [73] grid_4.3.3           ipred_0.9-14         colorspace_2.1-0    
## [76] nlme_3.1-166         cli_3.6.2            kableExtra_1.4.0    
## [79] fansi_1.0.6          viridisLite_0.4.2    svglite_2.1.3       
## [82] lava_1.8.0           gtable_0.3.4         broom.mixed_0.2.9.6 
## [85] sass_0.4.9           digest_0.6.35        htmlwidgets_1.6.4   
## [88] htmltools_0.5.8.1    lifecycle_1.0.4      hardhat_1.3.1       
## [91] MASS_7.3-60.0.1