I'm trying to create many different possible weighting schemes based on temperature.
I created a data frame with all possible combinations of 8 vectors (each vector represents a temperature range). So the columns of the data frame are a specific temperature range and the rows are weights.
I would like to pass the temperature ranges as arguments to case_when
, and loop through each row of the weights data frame, creating a new variable for each row based on the actual temperature and the associated weight for that temperature based on the information in the weights data frame.
Using the following post, I was able to create a function to produce the weights data frame:
Use dplyr::case_when with arguments programmatically
But I don't know how to construct the case_when
arguments using the weights data frame.
library(rlang)
library(tidyverse)
create_temp_weights <- function(
from = 31,
to = 100,
by = 10,
weights = exprs(between(., 31, 40) ~ c(0, 0.2),
between(., 41, 50) ~ c(0.5, 0.8),
between(., 51, 90) ~ c(0.8, 1),
between(., 91, 100) ~ c(0.2, 0.8),
TRUE ~ c(-0.1, 0))
) {
# use 999 to map other temperatures to last case
map(c(seq(from, to, by), 999), ~ case_when(!!!weights)) %>%
set_names(c(map_chr(seq(from, to, by),
~ str_c("temp_", ., "_", . + by - 1)), "temp_other")) %>%
cross_df(.)
}
temp_weights <- create_temp_weights()
test_tibble <- tibble(temp = seq_len(100))
head(test_tibble)
case_when
is the thing I'm trying to produce programmatically using the weights data frame.# Now I want to create a function that will produce the following
# case_when from the temp_weight data frame so I don't have to
# manually edit the following each time I create a new weights data frame
test_tibble2 <- map_dfc(.x = seq_len(nrow(temp_weights)),
~ transmute(
test_tibble,
temp =
case_when(
temp >= 31 & temp <= 40 ~ temp_weights$temp_31_40[.x],
temp >= 41 & temp <= 50 ~ temp_weights$temp_41_50[.x],
temp >= 51 & temp <= 60 ~ temp_weights$temp_51_60[.x],
temp >= 61 & temp <= 70 ~ temp_weights$temp_61_70[.x],
temp >= 71 & temp <= 80 ~ temp_weights$temp_71_80[.x],
temp >= 81 & temp <= 90 ~ temp_weights$temp_81_90[.x],
temp >= 91 & temp <= 100 ~ temp_weights$temp_91_100[.x],
TRUE & !is.na(temp) ~ temp_weights$temp_other[.x]
)
) %>% set_names(paste0("temp_wt_", .x))
)
head(test_tibble2)
So what I'm looking for is a function that constructs the case_when
arguments from a weights data frame.
case_when: A general vectorised if This function allows you to vectorise multiple if and else if statements. It is an R equivalent of the SQL CASE WHEN statement.
We can create a data frame using the data. frame() function. For example, the above shown data frame can be created as follows.
Closely mimicking OP:
windows <-
str_extract_all(names(temp_weights), "\\d+") %>%
modify(as.integer) %>%
modify_if(negate(length), ~ c(-Inf, Inf)) %>%
set_names(names(temp_weights))
temp <- test_tibble$temp
res <-
map_dfc(
seq_len(nrow(temp_weights)),
~ {
row <- .
rlang::eval_tidy(expr(case_when(
!!! imap(
windows,
~ expr(
between(temp, !! .x[1], !! .x[2]) ~ !! temp_weights[[.y]][row]
)
)
)))
}
) %>%
set_names(paste0("temp_wt_", seq_along(.)))
all.equal(res, test_tibble2)
#> [1] TRUE
Slightly more efficient (not repeating case_when
for each weight combination):
res2 <-
rlang::eval_tidy(expr(case_when(
!!! imap(
windows,
~ expr(
between(temp, !! .x[1], !! .x[2]) ~ list(!! temp_weights[[.y]])
)
)
))) %>%
do.call(what = rbind) %>%
as_tibble() %>%
set_names(paste0("temp_wt_", seq_along(.)))
all.equal(res2, test_tibble2)
#> [1] TRUE
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With